2
\$\begingroup\$

I am fairly new to Laravel and have implemented a many to many polymorphic relationship to a model where you have two tables that act as polymorphic references one is for content (text information that can be translated into various languages) and the other is for images that can be referenced by a model that belong to that item.

My question is with regards to my Controller's methods that I have written. Can my code be written better for example can I use eloquent queries instead of manipulating arrays. Are there better ways that this Controller may be written. Please review my code and advise on more optimal ways that this may be written. Do you propose I used the wrong table schema for the purpose? Your feedback is welcome

Following is the database tables relating to prior description.

    --
    -- Table structure for table `contents`
    --

    CREATE TABLE `contents` (
    `id` int(10) UNSIGNED NOT NULL,
    `body` text COLLATE utf8mb4_unicode_ci,
    `area` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `lang` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `contentable_id` int(11) NOT NULL,
    `contentable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `created_at` timestamp NULL DEFAULT NULL,
    `updated_at` timestamp NULL DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    --
    -- Dumping data for table `contents`
    --

    INSERT INTO `contents` (`id`, `body`, `area`, `lang`, `contentable_id`, `contentable_type`, `created_at`, `updated_at`) VALUES
    (5, 'en title', 'title', 'en', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05'),
    (6, '<p>en des</p>', 'description', 'en', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05'),
    (7, 'zh title', 'title', 'zh', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05'),
    (8, '<p>zh des</p>', 'description', 'zh', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05');

    -- --------------------------------------------------------

    --
    -- Table structure for table `images`
    --

    CREATE TABLE `images` (
    `id` int(10) UNSIGNED NOT NULL,
    `title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
    `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `imageable_id` int(11) NOT NULL,
    `imageable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `created_at` timestamp NULL DEFAULT NULL,
    `updated_at` timestamp NULL DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    --
    -- Dumping data for table `images`
    --

    INSERT INTO `images` (`id`, `title`, `path`, `imageable_id`, `imageable_type`, `created_at`, `updated_at`) VALUES
    (4, '2-1', '/storage/rooms/2/1.jpeg', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05'),
    (5, '2-2', '/storage/rooms/2/2.jpeg', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05'),
    (6, '2-3', '/storage/rooms/2/3.jpeg', 2, 'App\\Room', '2017-12-16 22:00:05', '2017-12-16 22:00:05');

    -- --------------------------------------------------------

This is my Room Controller that I have written which works all though I believe it can be more optimized.

    namespace App\Http\Controllers;

    use Illuminate\Http\Request;
    use DB;
    use App\Room;
    use App\Content;
    use App\Image;
    use Illuminate\Support\Facades\Storage;
    use Illuminate\Support\Facades\File;

    use Imagick;

    class RoomController extends Controller
    {
        /**
        * Display a listing of the resource.
        *
        * @return \Illuminate\Http\Response
        */
        public function index(Request $request)
        {
            $rooms = Room::orderBy('id','ASC')->paginate(5);
            return view('room.index',compact('rooms'))
                ->with('i', ($request->input('page', 1) - 1) * 5);
        }

        /**
        * Show the form for creating a new resource.
        *
        * @return \Illuminate\Http\Response
        */
        public function create()
        {
            return view('room.create');
        }


        /**
        * Store a newly created resource in storage.
        *
        * @param  \Illuminate\Http\Request  $request
        * @return \Illuminate\Http\Response
        */
        public function store(Request $request)
        {
            $this->validate($request, [
                'title' => 'required',
                'description' => 'required',
                'units' => 'required', 
                'sleeps' => 'required',
                'image_1' => 'mimes:jpeg,png,jpg,gif,svg',
                'image_2' => 'mimes:jpeg,png,jpg,gif,svg',
                'image_3' => 'mimes:jpeg,png,jpg,gif,svg'  
            ]);
            // Create a room
            $data = new Room;
            $data->title = $request->title;
            $data->description = $request->description;
            $data->units = $request->units;
            $data->sleeps = $request->sleeps;
            $data->sharing = $request->sharing;
            $data->save();
            // Save content and translations 
            // English     
            $titleE = $this->save_content($request->title, 'title', 'en');
            $data->contents()->save($titleE);
            $descrE = $this->save_content($request->description, 'description', 'en');
            $data->contents()->save($descrE);
            // Chinese
            $titleC = $this->save_content($request->title_zh, 'title', 'zh');
            $data->contents()->save($titleC);
            $descrC = $this->save_content($request->description_zh, 'description', 'zh');
            $data->contents()->save($descrC);

            // Save Images
            //Storage::makeDirectory('rooms/'.$data->id);
            if(!File::isDirectory(public_path().'/storage/rooms/'.$data->id))
            {
                $result = File::makeDirectory(public_path().'/storage/rooms/'.$data->id, 0775, true);
            }
            // Image 1
            if ($request->hasFile('image_1')) {
                $this->write_image($request->image_1->path(), $data->id, 1, $request->image_1->extension());
                $image = $this->save_image($data->id, 1, $request->image_1->extension());
                $data->images()->save($image);
            }
            // Image 2
            if ($request->hasFile('image_2')) {
                $this->write_image($request->image_2->path(), $data->id, 2, $request->image_2->extension());
                $image = $this->save_image($data->id, 2, $request->image_2->extension());
                $data->images()->save($image);
            }
            // Image 3
            if ($request->hasFile('image_3')) {
                $this->write_image($request->image_3->path(), $data->id, 3, $request->image_3->extension());
                $image = $this->save_image($data->id, 3, $request->image_3->extension());
                $data->images()->save($image);
            }
            return redirect()->route('dashboard.room.index')
                            ->with('success','Room created successfully');
        }

        /**
        * Display the specified resource.
        *
        * @param  int  $id
        * @return \Illuminate\Http\Response
        */
        public function show($id)
        {
            $room = Room::find($id);
            $content = $room->contents;
            $room->title_zh = $content[$this->get_key($content, 'title', 'zh', 'area', 'lang')]['body'];
            $room->description_zh = $content[$this->get_key($content, 'description', 'zh', 'area', 'lang')]['body'];
            return view('room.show', compact('room'));
        }

        /**
        * Show the form for editing the specified resource.
        *
        * @param  int  $id
        * @return \Illuminate\Http\Response
        */
        public function edit($id)
        {
            $room = Room::find($id);
            $content = $room->contents;
            $image = $room->images;
            $room->image_1 = $image[$this->get_key($image, $id.'-1', $id, 'title', 'imageable_id')]['path'];
            $room->image_2 = $image[$this->get_key($image, $id.'-2', $id, 'title', 'imageable_id')]['path'];
            $room->image_3 = $image[$this->get_key($image, $id.'-3', $id, 'title', 'imageable_id')]['path'];
            $room->title_zh = $content[$this->get_key($content, 'title', 'zh', 'area', 'lang')]['body'];
            $room->description_zh = $content[$this->get_key($content, 'description', 'zh', 'area', 'lang')]['body'];
            return view('room.edit', compact('room'));
        }

        /**
        * Update the specified resource in storage.
        *
        * @param  \Illuminate\Http\Request  $request
        * @param  int  $id
        * @return \Illuminate\Http\Response
        */
        public function update(Request $request, $id)
        {
            $this->validate($request, [
                'title' => 'required',
                'description' => 'required',
                'units' => 'required', 
                'sleeps' => 'required',
                'image_1' => 'nullable|mimes:JPG,JPEG,jpeg,png,jpg,gif,svg',
                'image_2' => 'nullable|mimes:JPG,JPEG,jpeg,png,jpg,gif,svg',
                'image_3' => 'nullable|mimes:JPG,JPEG,jpeg,png,jpg,gif,svg'  
            ]);

            $data = Room::find($id);
            $data->title = $request->title;
            $data->description = $request->description;
            $data->units = $request->units;
            $data->sleeps = $request->sleeps;
            $data->sharing = $request->sharing;
            $data->save();

            $this->update_content('title', 'en', 'App\Room', $id, $request->title);
            $this->update_content('description', 'en', 'App\Room', $id, $request->description);
            $this->update_content('title', 'zh', 'App\Room', $id, $request->title_zh);
            $this->update_content('description', 'zh', 'App\Room', $id, $request->description_zh);

            // Image 1
            if ($request->hasFile('image_1')) {
                $this->write_image($request->image_1->path(), $id, 1, $request->image_1->extension());

            }
            // Image 2
            if ($request->hasFile('image_2')) {
                $this->write_image($request->image_2->path(), $id, 2, $request->image_2->extension());

            }
            // Image 3
            if ($request->hasFile('image_3')) {
                $this->write_image($request->image_3->path(), $id, 3, $request->image_3->extension());
            }

            return redirect()->route('dashboard.room.index')
                            ->with('success','Room updated successfully');
        }
        /**
        * Remove the specified resource from storage.
        *
        * @param  int  $id
        * @return \Illuminate\Http\Response
        */
        public function destroy($id)
        {
            $room = Room::find($id);
            $room->images()->delete();
            $room->contents()->delete();
            File::deleteDirectory(public_path().'/storage/rooms/'.$id);
            $room->delete();
            return redirect()->route('dashboard.room.index')
                            ->with('success','Room deleted successfully');
        }

        // write images to storage
        private function write_image($path, $id, $filename, $ext){
            $img = new Imagick($path);
            $img->scaleImage(0,200);
            $img->writeImage(public_path('storage/rooms/'.$id.'/'.$filename.'.'.$ext));
            $img->destroy();
        }

        private function save_content($body, $area, $lan){
            $content = new Content;
            $content->body = $body;
            $content->area = $area;
            $content->lang =  $lan;
            return $content;
        }

        private function update_content($area, $lang, $type, $id, $body){
            $content = Content::where(
                [
                    ['area', $area],
                    ['lang', $lang],
                    ['contentable_type', $type],
                    ['contentable_id', $id]
                ]
            )->first();
            $content->body = $body;
            $content->save();
        }

        private function save_image($id, $fName, $fExt){
            $image = new Image;
            $image->title = $id.'-'.$fName;
            $image->path = '/storage/rooms/'.$id.'/'.$fName.'.'.$fExt;
            return $image;
        }

        private function get_key($array, $value1, $value2, $key1, $key2){
            $key = false;
            $search = [$key1 => $value1, $key2 => $value2];
            foreach ($array as $k => $v) {
                if ($v[$key1] == $search[$key1] && $v[$key2] == $search[$key2]) {
                    $key = $k;
                    // key found - break the loop
                    break;
                }
            }  
            return $key;
        }

    }

Following is the corresponding Models Room Model

    namespace App;

    use Illuminate\Database\Eloquent\Model;

    class Room extends Model
    {

        /**
        * Get all of the room's images.
        */
        public function images()
        {
            return $this->morphMany('App\Image', 'imageable');
        }

        /**
        * Get all of the room's content.
        */
        public function contents()
        {
            return $this->morphMany('App\Content', 'contentable');
        }

        /**
        * The bookings that belong to the room.
        */
        public function bookings()
        {
            return $this->belongsToMany('App\Booking');
        }    
    }

Content Model

    namespace App;

    use Illuminate\Database\Eloquent\Model;

    class Content extends Model
    {

        /**
        * Get all of the owning contentable models.
        */
        public function contentable()
        {
            return $this->morphTo();
        }

        /**
        * Get all of the rooms that are assigned this content.
        */
        public function rooms()
        {
            return $this->morphedByMany('App\Room', 'contentable');
        }    

    }

Image Model

    namespace App;

    use Illuminate\Database\Eloquent\Model;

    class Image extends Model
    {
        //


        /**
        * Get all of the owning imageable models.
        */
        public function imageable()
        {
            return $this->morphTo();
        }

        /**
        * Get all of the rooms that are assigned this image.
        */
        public function rooms()
        {
            return $this->morphedByMany('App\Room', 'imageable');
        }

    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.