بعد أن انتهينا من إعداد المشروع وإنشاء توابع وعروض الإنشاء والتخزين والعرض، سوف نقوم في درسنا الأخير اليوم بكتابة الشيفرة البرمجية لتوابع التعديل والتحديث والحذف ومن ثم العروض الموافق لها.
تابع 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.
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.