لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 01/03/16 in مقالات البرمجة
-
بعد أن انتهينا من إعداد المشروع وإنشاء توابع وعروض الإنشاء والتخزين والعرض، سوف نقوم في درسنا الأخير اليوم بكتابة الشيفرة البرمجية لتوابع التعديل والتحديث والحذف ومن ثم العروض الموافق لها. تابع Editإن توابع وعروض edit وdelete غير موجودة لحد الساعة، فلننشئها، سنبدأ بالعمل على edit أولا. عدل على تابع المتحكم: public function edit($id) { $marketingImage = Marketingimage::findOrFail($id); return view('marketingimage.edit', compact('marketingImage')); }مرة أخرى قمنا باستخدام تابع findOrFail وقمنا بإرسال الكائن إلى العرض لأننا نريد أن نقوم بتعبئة الاستمارة مسبقا بالقيم المناسبة من سجلات البيانات. عرض Editعدل على ملف views/marektingimage/edit.blade.php ليكون كالتالي: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', "edit $marketingImage->image_name.$marketingImage->image_extension" ]) !!} <h1>Edit {{ $marketingImage->image_name. '.' . $marketingImage->image_extension }} </h1> <hr/> @if (count($errors) > 0) <div class="alert alert-danger"> <strong>Whoops! </strong> There were some problems with your input. <br> <br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }} </li> @endforeach </ul> </div> @endif <div> Note: name and path values cannot be changed. If you wish to change these, then delete and create a new photo: </div> <br> {!! Form::model($marketingImage, ['route' => ['marketingimage.update', $marketingImage->id], 'method' => 'PATCH', 'class' => 'form', 'files' => true] ) !!} <!-- image name Form Input --> <div> <ul> <li> <h4>Image Name: {{ $marketingImage->image_name. '.' . $marketingImage->image_extension }} </h4> </li> <li> <h4>Image Path: {{ $marketingImage->image_path }} </h4> </li> <li> <h4>Mobile Name: {{ $marketingImage->mobile_image_name. '.' . $marketingImage->mobile_extension }} </h4> </li> <li> <h4>Mobile Path: {{ $marketingImage->mobile_image_path }} </h4> </li> </ul> </div> <!-- is_something Form Input --> <div class="form-group"> {!! Form::label('is_active', 'Is Active:') !!} {!! Form::checkbox('is_active') !!} </div> <!-- is_featured Form Input --> <div class="form-group"> {!! Form::label('is_featured', 'Is Featured:') !!} {!! Form::checkbox('is_featured') !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('image', 'Primary Image') !!} {!! Form::file('image', null, array('class'=>'form-control')) !!} </div> <!-- form field for file --> <div class="form-group"> {!! Form::label('mobile_image', 'Mobile Image') !!} {!! Form::file('mobile_image', null, array('class'=>'form-control')) !!} </div> <div class="form-group"> {!! Form::submit('Edit', array('class'=>'btn btn-primary')) !!} </div> {!! Form::close() !!} <div> {!! Form::model($marketingImage, ['route' => ['marketingimage.destroy', $marketingImage->id], 'method' => 'DELETE', 'class' => 'form', 'files' => true] ) !!} <div class="form-group"> {!! Form::submit('Delete Photos', array('class'=>'btn btn-danger', 'Onclick' => 'return ConfirmDelete();')) !!} </div> {!! Form::close() !!} </div> @endsection @section('scripts') <script> function ConfirmDelete() { var x = confirm("Are you sure you want to delete?"); if (x) return true; else return false; } </script> @endsectionلم نسمح للمستخدم بتعديل أسماء الصور أومساراتها لأننا نريدهم أن يقوموا بحذف السجلات والبدء من جديد وإلا سيكون الأمر معقدا عندما نقوم بتعديل الأسماء التي قمنا بحفظها في السجلات وهذا الأمر لا يدخل في نطاق الدرس. ماهي الاختلافات الأخرى في الاستمارة ؟ سوف تلاحظ أننا نستخدم حقول أقل وقمنا باستخدام الربط بين النموذج والاستمارة في مساعدي الاستمارة (form helper): {!! Form::model($marketingImage, ['route' => ['marketingimage.update', $marketingImage->id], 'method' => 'PATCH', 'class' => 'form', 'files' => true] ) !!}إن مثيل النموذج marketingImage$ مرتبط بالاستمارة لذلك سيتم تعبئة الحقول بشكل مسبق، ولاحظ أننا قمنا أيضا بتحديد marketingImage->id$ حتى نتمكن من إرسال ذلك إلى تابع المتحكم وتعديل السجل الصحيح. ولقد قمنا بتعيين قيمة PATCH إلى method وهذه سوف تتغير تلقائيا لأن HTML لا يدعم PATCH، ولقد قمنا بتعيين قيمة true إلى files حتى نتمكن من إرسال الملفات. بقية الشيفرة سهلة وقمنا بشرحها سابقا، لاحظ أننا قمنا بوضع زر للحذف في الأسفل في حالة ما أراد المستخدم الحذف بدل التعديل. عند سير العمل بطريقة عادية، سوف تكون خطوتك القادمة في الغالب الانتقال إلى تابع update في MarketingImagesController لكننا سنقوم بإنشاء صنف request للتعامل مع سيناريو update. EditImageRequestالفرق في عملية التحقق بين create و update هو أنه عندما تقوم بإنشاء صورة سيكون ملف الصورة إلزاميا على عكس التحديث، لذلك قررت لأجل البساطة والوضوح أن نقوم بإنشاء صنف request منفصل باسم EditImageRequest بدلا من وضعه في صنف request واحد. المشكلة التي واجهتها هي أنني لا أعرف كيف أجعلها شرطية فعلى أي تابع يجب وضع الصنف، لذلك بدأت في التفكير في الأمر، فبدلا من ذلك، يمكنني أن أقوم بتمرير حقل خفي والذي سوف يُعرف الاستمارة وسوف أقوم بوضع فيه عنصر تحكم منطقي (controlling logic)، فإذا كانت الاستمارة استمارة create، فستكون الصور إلزامية، لكنني في النهاية قررت أنه من الأسهل أن أقوم بعمل صنف منفصل. لذلك قم بتنفيذ هذا الأمر من سطر الأوامر: php artisan make:request EditImageRequestثم قم بتعيين قيمة return true في تابع authorize في ذلك الصنف وعدل تابع rules إلى التالي: public function rules() { return [ 'is_active' => 'boolean', 'is_featured' => 'boolean', 'image' => 'mimes:jpeg,jpg,bmp,png | max:1000', 'mobile_image' => 'mimes:jpeg,jpg,bmp,png | max:1000' ]; }قمنا بالفعل بالتحدث عن تابع rules سابقا ولا داعي لإعادة شرحه هنا. تابع Updateلنقم الآن بالانتقال إلى تابع update في MarketingImageController، عدله كما يلي: public function update($id, EditImageRequest $request) { $marketingImage = Marketingimage::findOrFail($id); $marketingImage->is_active = $request->get('is_active'); $marketingImage->is_featured = $request->get('is_featured'); $this->formatCheckboxValue($marketingImage); $marketingImage->save(); if ( ! empty(Input::file('image'))){ $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension(); //create instance of image from temp upload $image = Image::make($file->getRealPath()); //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension); } if ( ! empty(Input::file('mobile_image'))) { $destinationMobile = '/imgs/marketing/mobile/'; $mobileFile = Input::file('mobile_image'); $mobileImageName = $marketingImage->mobile_image_name; $mobileExtension = $request->file('mobile_image')->getClientOriginalExtension(); //create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension); } flash()->success('image edited!'); return view('marketingimage.edit', compact('marketingImage')); }دعونا نبدأ بتوقيع التابع: public function update($id, EditImageRequest $request) {سوف ترى أننا نقوم بسحب مثيل من كائن request الصحيح، وقمنا باستخدام findOrFail في نموذج السجل حتى نتمكن من تعيين القيم من مثيل request، وبعد ذلك قمنا بتهيئة (format) قيم خانة الاختيار (checkbox) ثم حفظناها: $marketingImage = Marketingimage::findOrFail($id); $marketingImage->is_active = $request->get('is_active'); $marketingImage->is_featured = $request->get('is_featured'); $this->formatCheckboxValue($marketingImage); $marketingImage->save();إذا لم تكن الصورة الأولية فارغة، سوف نقوم بالتحديث: if ( ! empty(Input::file('image'))){ $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension(); //create instance of image from temp upload $image = Image::make($file->getRealPath()); //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension); }ثم سنفعل نفس الشيء على صور الهاتف: if ( ! empty(Input::file('mobile_image'))) { $destinationMobile = '/imgs/marketing/mobile/'; $mobileFile = Input::file('mobile_image'); $mobileImageName = $marketingImage->mobile_image_name; $mobileExtension = $request->file('mobile_image')->getClientOriginalExtension(); //create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension); }ثم قمنا بتنفيذ flash لـ success وبعد ذلك قمنا بالعودة return وفي حالتنا هذه إلى صفحة التعديل edit، لكن يمكنك تغيير العودة إلى أي صفحة تريدها: flash()->success('image edited!'); return view('marketingimage.edit', compact('marketingImage'));أرجو ملاحظة أنه إذا كنت تقوم بعمل هذا لشركة تطوير، ربما قد تريد أن تجعل هذه الشيفرة أقوى عن طريق التحقق من أن الملف قد تم إنشاءه، وسوف تتعامل مع الأمر بشكل مختلف إذا لم يعمل لبعض الأسباب، لكن كل هذا خارج نطاق الدرس. حسنا، الآن آخر خطوة في درسنا. تابع Destroyسوف نحتاج إلى كتابة تابع destroy للتعامل مع حذف الصور: public function destroy($id) { $marketingImage = Marketingimage::findOrFail($id); $thumbPath = $marketingImage->image_path.'thumbnails/'; File::delete(public_path($marketingImage->image_path). $marketingImage->image_name . '.' . $marketingImage->image_extension); File::delete(public_path($marketingImage->mobile_image_path). $marketingImage->mobile_image_name . '.' . $marketingImage->mobile_extension); File::delete(public_path($thumbPath). 'thumb-' . $marketingImage->image_name . '.' . $marketingImage->image_extension); Marketingimage::destroy($id); flash()->success('image deleted!'); return redirect()->route('marketingimage.index'); }يمكنك أن ترى أننا قمنا باستخدام findOrFail على id الذي تم استلامه عن طريق التوقيع، ثم استخدمنا مساعدي الملف (File helper) لـ Laravel للحذف، وهكذا تحصلنا مرة أخرى على صياغة جميلة لتبين لنا ما نقوم بفعله. قمنا أيضا باستخدام تابع ()public_path ووضعنا مكونات الصورة داخل ذلك التوقيع، وبعد ذلك قمنا بتكرار نفس الأمر مع كل نوع من الصور مرتبط مع السجل، ثم استخدمنا تابع destroy لحذف السجل من قاعدة البيانات، وقمنا بتنفيذ flash لتابع success ومن ثم قمنا بإعادة التوجيه إلى صفحة index، وهاقد انتهينا. خاتمةلقد تعلمنا في هذه الدروس العديد من الطرق والمفاهيم الجديدة في Laravel، وعلى الرغم من بساطة التطبيق الذي قمنا بعمله إلا أنه سيكون بداية جيدة لاحتراف إدارة الصور في Laravel، وإذا كان لديك أي سؤال أو تعليق، فيسرنا أن نسمعه في التعليقات في الأسفل، وإذا أعجبتك هذه التدوينة، أرجو أن تقوم بمشاركتها مع أصدقاءك. ترجمة -وبتصرّف- للدرس Basic Image Management Part 3 لصاحبه Bill Keck. حقوق الصورة البارزة: Designed by Freepik.1 نقطة
-
في الحالة العادية، سوف تقوم بكتابة تابع (store method) أولا، ومن ثم تترك عملية التحقق في النهاية حتى يعمل store بشكل جيد، لكن في حالتنا هذه سيكون الأمر معقدا قليلا بما أننا نتعامل مع ملفات منفصلة يجب حفظها، ففي حالة الصور، سوف نحتاج إلى التحقق أولا. صنف Requestسوف نبدأ بإنشاء صنف (request class) والذي سيقوم بالتعامل مع عملية التحقق من استمارة الإنشاء. سوف نبدأ بكتابة هذا الأمر على سطر الأوامر: php artisan make:request CreateImageRequestهذا الأمر سيقوم بإنشاء الملف ووضعه داخل مجلد app/Http/Requests. بعد ذلك، عدل الملف كالتالي: <?php namespace App\Http\Requests; use App\Http\Requests\Request; use App\Marketingimage; class CreateImageRequest extends Request { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'image_name' => 'alpha_num | required | unique:marketing_images', 'mobile_image_name' => 'alpha_num | required | unique:marketing_images', 'is_active' => 'boolean', 'is_featured' => 'boolean', 'image' => 'required | mimes:jpeg,jpg,bmp,png | max:1000', 'mobile_image' => 'required | mimes:jpeg,jpg,bmp,png | max:1000' ]; } }واحد من الأشياء التي أحبها في إطار Laravel هو كيفية تعامله مع عملية التحقق، فإذا كانت هذه المرة الأولى التي ستتعامل فيها مع عملية التحقق، سوف تجد أن هذه العملية بسيطة للغاية. لاحظ أننا قمنا بكتابة السطر التالي في تابع authorize: return true;هذا السطر سوف يسمح للطلب أن يمر، فإذا وضعنا شرطا عليه فسنستطيع التحكم فيه، فعلى سبيل المثال، يمكننا استخدامه في تسجيل الدخول للأعضاء فقط. بعد ذلك لدينا قواعد التابع، الذي سوف يتم فرضها على عملية التحقق في حقول الاستمارة، لن نتعمق أكثر هنا، فإذا أردت المزيد يمكنك الإطلاع على توثيق Laravel. للتذكير فإننا نفصل ما بين قواعد التّحقق بمحرف "|" ولما نضيف قاعدة "unique" فإننا نُرفقها باسم الجدول الذي يجب أن نبحث فيه (يعني الجدول الذي نرغب في أن يكون السّجل record الذي نقوم بحفظه وحيدًا "unique"). ومن الطبيعي أننا أننا قمنا بإضافة قيد على حجم الملفات. يمكنك إيجاد قائمة كاملة من قواعد التحقق في الوثائق. تابع Storeعلى أي حال، سوف ننتقل الآن لتابع store في MarketingImageController، عدل التابع كما يلي: public function store(CreateImageRequest $request) { //create new instance of model to save from form $marketingImage = new Marketingimage([ 'image_name' => $request->get('image_name'), 'image_extension' => $request->file('image')->getClientOriginalExtension(), 'mobile_image_name' => $request->get('mobile_image_name'), 'mobile_extension' => $request->file('mobile_image')->getClientOriginalExtension(), 'is_active' => $request->get('is_active'), 'is_featured' => $request->get('is_featured'), ]); //define the image paths $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $destinationMobile = '/imgs/marketing/mobile/'; //assign the image paths to new model, so we can save them to DB $marketingImage->image_path = $destinationFolder; $marketingImage->mobile_image_path = $destinationMobile; // format checkbox values and save model $this->formatCheckboxValue($marketingImage); $marketingImage->save(); //parts of the image we will need $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension(); //create instance of image from temp upload $image = Image::make($file->getRealPath()); //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension); // now for mobile $mobileFile = Input::file('mobile_image'); $mobileImageName = $marketingImage->mobile_image_name; $mobileExtension = $request->file('mobile_image')->getClientOriginalExtension(); //create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension); // Process the uploaded image, add $model->attribute and folder name flash()->success('Marketing Image Created!'); return redirect()->route('marketingimage.show', [$marketingImage]); }حسنا، دعونا نبدأ بفهم هذه الشفرة الطويلة، سنبدأ مع توقيع الدالة: public function store(CreateImageRequest $request)هذا الجزء واضح، فلقد قمنا فقط بسحب نسخة (instance) من كائن request يحمل الاسم request$ وهذا سوف يساعدنا على إنشاء نسخة جديدة للنموذج: //create new instance of model to save from form $marketingImage = new Marketingimage([ 'image_name' => $request->get('image_name'), 'image_extension' => $request->file('image')->getClientOriginalExtension(), 'mobile_image_name' => $request->get('mobile_image_name'), 'mobile_extension' => $request->file('mobile_image')->getClientOriginalExtension(), 'is_active' => $request->get('is_active'), 'is_featured' => $request->get('is_featured'), ]);لاحظ أنني أقوم بالتعليق كثيرا على هذا التابع (method)، لأنه من السهل أن يربكك. على أي حال، قمنا بإنشاء مثيل جديد لـ Marketingimage ووضعنا خصائص النموذج باستخدام مثيل request$. أما البقية الشيفرة، فلقد قُمنا بسحب البيانات من الاستمارة إذا نجحت في عملية التحقق. ثم سوف نحتاج إلى تعريف مسارات الصور: //define the image paths $destinationFolder = '/imgs/marketing/'; $destinationThumbnail = '/imgs/marketing/thumbnails/'; $destinationMobile = '/imgs/marketing/mobile/';ملاحظة: حاولت أن أجعل هذه الأسماء بديهية قدر الإمكان. بعد ذلك، سوف نقوم بتعيينها إلى النموذج حتى نتمكن من حفظهم في قاعدة البيانات. //assign the image paths to new model, so we can save them to DB $marketingImage->image_path = $destinationFolder; $marketingImage->mobile_image_path = $destinationMobile;نستطيع فعل هذا بطريقة أخرى، فيمكننا أن نقوم بتقديم معلومات المسار في الاستمارة أو يمكننا تخطي هذا كليا، لكن وجدت أن إبقاء المسار في قاعدة البيانات يجعل العمل مع الصور أسهل عندما نحتاج إلى استخدامها في العروض (views)، وهذا هو سبب استخدامنا هذه الطريقة. ثم سنقوم بتهيئة (format) قيم خانة اختيار ومن ثم نقوم بحفظها: // format checkbox values and save model $this->formatCheckboxValue($marketingImage); $marketingImage->save(); يمكنك أن ترى أنني أقوم بتسليم مثيل النموذج marketingImage$ إلى تابع formatCheckboxValue، وهو تابع قمت بعمله للتأكد من أن خانة الاختيار يتم التعامل معها بشكل جيد: public function formatCheckboxValue($marketingImage) { $marketingImage->is_active = ($marketingImage->is_active == null) ? 0 : 1; $marketingImage->is_featured = ($marketingImage->is_featured == null) ? 0 : 1; }بسبب نوع البيانات في قاعدة البيانات، يمكننا توفير فقط رقمين 0 و 1، لذلك نحتاج إلى التأكد من أننا نقوم بالتحويل بشكل صحيح من خانة الاختيار (checkbox) في الاستمارة (form). لقد قمنا بتسليمها إلى مثيل النموذج ومن ثم سيقوم بوضع الخصائص وفقا لذلك. بعد ذلك نعود إلى تابع store، ونقوم ببساطة بالحفظ: $marketingImage->save();وهذا سوف يهتم بسجلات قاعدة البيانات، ومع ذلك، لانزال بحاجة إلى التعامل مع الملف. في البداية سوف نعمل على بعض أجزاء الصورة التي سنحتاجها: //parts of the image we will need $file = Input::file('image'); $imageName = $marketingImage->image_name; $extension = $request->file('image')->getClientOriginalExtension();بدلا من ذلك، يمكنك جعل بعض هذه الشفرات في سطر واحد (inline) لكنك ستحصل على سطور طويلة جدا بالإضافة إلى أن إبقائها هكذا أسهل للتتبع والفهم. الآن سوف نحتاج إلى استخدام مكتبة الصور (Image library) لمساعدتنا على الخروج من هنا، سنقوم بإنشاء مثيل لهذه الصورة من الصور المرفوعة: //create instance of image from temp upload $image = Image::make($file->getRealPath());بعد ذلك، سنقوم بحفظها وإنشاء صورة مصغرة والتي سنقوم بحفظها عن طريق سَلسَلة التوابع (chaining the methods): //save image with thumbnail $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension) ->resize(60, 60) // ->greyscale() ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension);سوف نقوم بتقسيم هذه الشيفرة إلى أجزاء حتى نفهمها بشكل أفضل، أول جزء يقوم بحفظ الصور الأولية: $image->save(public_path() . $destinationFolder . $imageName . '.' . $extension)استخدمنا ()public_path للوصول إلى المكان الذي يمكننا تعريف فيه مجلد الهدف (مجلد الصور) والذي هو imgs/marekting/. بعد ذلك قمنا بسَلسَلة أجزاء الصورة (مسار ملف الصورة) التي نحتاجها وهذا سوف يعطينا كل شيء نحتاجه وسيسهل علينا عملية متابعة الشيفرة البرمجية. بما أننا نحتاج أيضا إلى إنشاء صورة مصغرة، قمنا بسَلسَلة ذلك التابع: ->resize(60, 60)وهذا السطر سوف ينشئ لنا نسخة بحجم 60 × 60 من الصورة، وإذا أردت تغيير حجم النسخة يمكنك فعل هذا بسهولة بتغيير الأرقام، ويمكنك أيضا إنشاء حقل في الاستمارة للمستخدمين لتحديد ارتفاع و عرض الصور المصغر. (لن نقوم بهذا في هذا الدرس.) قمتُ بإرفاق (على شكل تعليق) تابع متسَلسَل لجعل الصورة المصغرة ذات تدرج رمادي، فإذا أردت صور مصغرة بيضاء وسوداء اجعل السطر شيفرة برمجية (عن طريق إزالة //). والآن سنقوم بحفظ الصورة المصغرة: ->save(public_path() . $destinationThumbnail . 'thumb-' . $imageName . '.' . $extension);وسنقوم بنفس الشيء لصور الهاتف: // create instance of image from temp upload $mobileImage = Image::make($mobileFile->getRealPath()); $mobileImage->save(public_path() . $destinationMobile . $mobileImageName . '.' . $mobileExtension);يما أنها مثل الصور الأولية لكن بدون صورة مصغرة، سوف نتركها هكذا. حسنا، بعد ذلك لدينا رسالة الفلاش (flash message): flash()->success('Marketing Image Created!');هذه الشيفرة لن تعمل إلا إذا قمت بتثبيت حزمة الفلاش لجفري ويِ (Jefferey Wey) وقمت باستدعائها في عرض (view) الصفحة الرئيسية، وإذا لم تقم بتثبيت هذه الحزمة فلا تكتب هذه الشيفرة. في النهاية، بعد حفظ كُل من النموذج وملفات الصور، سوف نقوم بإعادة التوجيه نحو صفحة العرض: return redirect()->route('marketingimage.show', [$marketingImage]);الآن يمكنك تجربة رفع وحفظ الصور وعمل صورة مصغرة وصورة للهاتف عن طريق زيارة الاستمارة في العنوان التالي: yourdomain.com/marketingimage/createيمكنك التأكد من إنشاء الصور من خلال النظر إلى المجلدات حيث يُفترض أن تكون الصور. سوف ترى أن تابع store يقوم بالكثير من الأشياء، لكن ينقصه آلية للتحقق من الأخطاء، يمكنك فعل هذا لجعل التطبيق أكثر قوة على الرغم من أنني أعتقد أن أمر ليس ضروري. بما أن Laravel يقوم بوظيفة جيدة عن طريق فصل استمارة التحقق من تابع store، لن يتوقف التابع إذا فشلت عملية التحقق. حسنا ماذا يمكن أن يفشل أيضا ؟ حسنا ربما تفشل عملية حفظ الملف وهذا يمكن أن يكون بسبب امتلاء النظام، أو بسبب مشاكل في الصلاحيات أو خطأ في كتابة المسارات وأسماء المجلدات. هذه الأخطاء صعبة الاكتشاف والتصحيح بسبب أنه ربما قد لا تحصل على رسائل الخطأ المناسبة، وعلى أية حال، هذه هي أشهر الأخطاء إذا أردت اكتشافها وتصحيحها. يمكنك أن ترى أنه على الرغم من أنه لا شيء صعب في إدارة الصورة إلا أن الصعوبة موجودة بشكل ما. الخطوة المنطقية التالية هي إنشاء تابع show في المتحكم وإنشاء العرض الخاص به. تابع showحسنا، هذه هي شيفرة تابع العرض: public function show($id) { $marketingImage = Marketingimage::findOrFail($id); return view('marketingimage.show', compact('marketingImage')); }هذه الشيفرة واضحة للغاية، يقوم Laravel باسترجاع مثيل النموذج ثم يقوم بإرجاعه إلى العرض view، لاحظ استخدام findOrFail والتي تقوم بإرجاع ModelNotFoundException، والتي يمكنك التعامل معها من خلال ملف Handler.php في app/Exceptions. عرض showحسنا، بعدما انتهينا من التابع ننتقل إلى العرض view: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage', "show $marketingImage->image_name.$marketingImage->image_extension" ]) !!} <div> {{ $marketingImage->image_name }} : <br> <img src="/imgs/marketing/{{ $marketingImage->image_name . '.' . $marketingImage->image_extension . '?'. 'time='. time() }}"> </div> <div> {{ $marketingImage->image_name }} - thumbnail : <br> <img src="/imgs/marketing/thumbnails/{{ 'thumb-' . $marketingImage->image_name . '.' . $marketingImage->image_extension . '?'. 'time='. time() }}"> </div> <div> {{ $marketingImage->mobile_image_name }} - mobile : <br> <img src="/imgs/marketing/mobile/{{ $marketingImage->mobile_image_name . '.' . $marketingImage->mobile_extension . '?'. 'time='. time() }}"> </div> @endsectionملاحظة: يمكنك إزالة مساعدات Breadcrumb إذا لم تكن تستخدم تلك الحزمة. لم نجعل الواجهة الأمامية فاخرة هنا، قمنا فقط بعمل صفحة بسيطة لإظهار الصور. وبما أننا أرسلنا مثيل marketingImage$ إلى المتحكم، فإننا استخدمنا تركيبة blade لاستدعاء اسم الصورة: {{ $marketingImage->image_name }}ثم نقوم بإستدعاء الصور الأولية: <img src="/imgs/marketing/{{ $marketingImage->image_name . '.' . $marketingImage->image_extension . '?'. 'time='. time() }}">سترى أننا استخدمنا تركيبة blade طباعة اسم الصورة وامتدادها والتي هي مسجلة في قاعد البيانات، وبإبقاء الامتداد في قاعدة البيانات يمكننا استخدام امتدادات مختلفة بدلا من فرض استخدام jpg. على سبيل المثال. سوف تلاحظ أيضا أننا أضفنا: '?'. 'time='. time() للحصول على متغير، وهذا الأمر سيمنع عملية تخزين المؤقت للصور نظرا لأنه تم إلحاق الوقت في نهاية الرابط. إن التخزين المؤقت للصور يمكن أن يكون مشكلة بالنسبة لإدارة الصور عندما تقوم برفع الصور. وبالطبع هذا الأمر اختياري ويمكنك تجاوزه. بقية الشيفرة البرمجية تقريبا نفسها مع بعض التغييرات للصور المصغرة والصور الهواتف. لن تكون الصفحة جميلة جدا، لكنها تقوم بالمهمة. والآن إذا كان لديك صورة قمت برفعها لحفظها في قاعدة البيانات، يمكنك مشاهدتها عن طريق الذهاب إلى: yourproject.com/marketingimage/1يمكنك تغيير الرقم في النهاية لأن هذا الرقم هو رقم الصورة في سجل البيانات. حسنا، بعد ذلك، قبل أن نعمل على edit و update، لنقم ببناء تابع index في المتحكم ثم عرض (index view). تابع indexفي MarketingImageController، عدل تابع index كما يلي: public function index() { $images = Marketingimage::all(); return view('marketingimage.index', compact('images')); }يمكنك أن ترى أن الشيفرة سهلة للغاية، فلقد قمنا فقط باسترجاع جميع السجلات إلى كائن images$، والتي يتم تمريرها إلى العرض view عن طريق تابع compact. عرض Indexبعد ذلك، سنقوم بتعديل views/marketingimages/index.blade.php كما يلي: @extends('layouts.master') @section('content') {!! Breadcrumb::withLinks(['Home' => '/', 'marketing images' => '/marketingimage']) !!} <br> <div> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">List of Marketing Images </div> <div class="panel-body"> <a href="/marketingimage/create"> <button type="button" class="btn btn-lg btn-success">Create New </button> </a> </div> <!-- Table --> <table class="table"> <tr> <th>Id </th> <th>Name </th> <th>Thumbnail </th> <th>Edit </th> <th>Delete </th> </tr> @foreach($images as $image ) <tr> <td>{{ $image->id }} </td> <td>{{ $image->image_name }} </td> <td> <a href="/marketingimage/{{ $image->id }}"> <img src="/imgs/marketing/thumbnails/{{ 'thumb-'. $image->image_name . '.' . $image->image_extension . '?'. 'time='. time() }}"> </a> </td> <td><a href="/marketingimage/{{ $image->id }}/edit"> <span class="glyphicon glyphicon-edit" aria-hidden="true"> </span> </a> </td> <td>{!! Form::model($image, ['route' => ['marketingimage.destroy', $image->id], 'method' => 'DELETE']) !!} <div class="form-group"> {!! Form::submit('Delete', array('class'=>'btn btn-danger', 'Onclick' => 'return ConfirmDelete();')) !!} </div> {!! Form::close() !!} </td> </tr> @endforeach </table> </div> </div> @endsection @section('scripts') <script> function ConfirmDelete() { var x = confirm("Are you sure you want to delete?"); if (x) return true; else return false; } </script> @endsectionلقد بدأنا بتوسيع layouts.master ومن ثم قمنا بفتح content كما يلي: @section('content')ومن ثم استخدمنا Breadcrumb. (مرة أخرى، إذا كنت لا تستخدم الحزمة فاحذف هذا السطر.) بعد ذلك قمنا باستخدام شيفرات Bootstrap أساسية، والتي حصلنا عليها من الموقع الرسمي. وفي النهاية سنجد جدول داخل مُكوّن panel لـ bootstrap بالإضافة إلى زر لإنشاء صورة جديدة، وأزرار أخرى للتعديل والحذف لكل صورة، وستظهر الصور المصغرة لكل صورة في الجدول. سوف ترى في رأسية مُكوّن panel كيف قمنا بالتعامل مع زر create: <a href="/marketingimage/create"> <button type="button" class="btn btn-lg btn-success"> Create New </button> </a>كل شيء واضح، فالجدول ليس معقدا، لكنه تطلب حلقة foreach لطباعة جميع الصفوف (rows): <!-- Table --> <table class="table"> <tr> <th>Id </th> <th>Name </th> <th>Thumbnail </th> <th>Edit </th> <th>Delete </th> </tr> @foreach($images as $image ) <tr> <td>{{ $image->id }} </td> <td>{{ $image->image_name }} </td> <td> <a href="/marketingimage/{{ $image->id }}"> <img src="/imgs/marketing/thumbnails/{{ 'thumb-'. $image->image_name . '.' . $image->image_extension . '?'. 'time='. time() }}"> </a> </td> <td> <a href="/marketingimage/{{ $image->id }}/edit"> <span class="glyphicon glyphicon-edit" aria-hidden="true"> </span> </a> </td> <td>{!! Form::model($image, ['route' => ['marketingimage.destroy', $image->id], 'method' => 'DELETE' ]) !!} <div class="form-group"> {!! Form::submit('Delete', array('class'=>'btn btn-danger', 'Onclick' => 'return ConfirmDelete();')) !!} </div> {!! Form::close() !!} </td> </tr> @endforeach </table>لا توجد أية صعوبة في الشيفرة، يمكنك أن ترى أنني أضفت مساعدات النموذج form helper حول رابط الحذف delete، وهذا لأسباب أمنية، يجب علينا الحذف باستخدام POST بدل السماح بأن تكون مفتوحة بشكل واسع من خلال استعمال GET. سوف تلاحظ أيضا أننا قمنا باستخدام التالي: 'Onclick' => 'return ConfirmDelete();'والتي استخدمناها مع الجافاسكربت لفتح صندوق التأكيد confirm box، حتى لا يقوم المستخدمون بحذف شيء عن طريق الخطأ. ثم في ('section('scripts@ أضفنا شيفرات جافاسكربت التالية، وكملاحظة سريعة، افترضنا أنه لديك ('yeld('scripts@ في صفحتك الرئيسية (master page)، وإذا لم يكن لديك هذا السطر، يمكنك ببساطة إرفاق الجافاسكربت قبل endsection@ التابعة لـ ('section('content@. هذه هي شيفرة الجافاسكربت: <script> function ConfirmDelete() { var x = confirm("Are you sure you want to delete?"); if (x) return true; else return false; } </script>شيفرة بسيطة للغاية. والآن، يجب أن تحصل على صفحة index تعمل بدون مشاكل، قم بتجربة ذلك عن طريق الذهاب بمتصفحك إلى: yourproject.com/marketingimageنُكمل في الجزء الثالث من الدرس إن شاء الله. ترجمة -وبتصرّف- للمقال Basic Image Management Part 2 لصاحبه Bill Keck. حقوق الصورة البارزة: Designed by Freepik.1 نقطة