اذهب إلى المحتوى

أساسيات إدارة الصور في 5 Laravel - الجزء الثالث


هشام رزق الله

بعد أن انتهينا من إعداد المشروع وإنشاء توابع وعروض الإنشاء والتخزين والعرض، سوف نقوم في درسنا الأخير اليوم بكتابة الشيفرة البرمجية لتوابع التعديل والتحديث والحذف ومن ثم العروض الموافق لها.

laravel5-image-management.thumb.png.5c3f

تابع 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.


تفاعل الأعضاء

أفضل التعليقات

السلام عليكم 

قمت بإدخال صوره و عمل اظهار لكل الصور في داله index و عند الضغط على الصوره يقوم بنقلك الى داله show لعرض الصوره و تفاصيلها 

لكن صادفتني مشكله في داله show حيث انه يعرض كافه التفاصيل الا الصوره نفسها 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

السلام عليكم 

قمت بإدخال صوره و عمل اظهار لكل الصور في داله index و عند الضغط على الصوره يقوم بنقلك الى داله show لعرض الصوره و تفاصيلها 

لكن صادفتني مشكله في داله show حيث انه يعرض كافه التفاصيل الا الصوره نفسها 

اكيد مسار الصورة خطأ تاكد من مسار الصورة في ال blade 

رابط هذا التعليق
شارك على الشبكات الإجتماعية



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...