Search code examples
phplaravelurlurl-routingslug

change naming convention of the url


I want to change the naming convention of the following url :

http://example.org/designs/CV%20Designs

To be the following:

http://example.org/designs/cv-designs

Here is my web.php route file:

    Route::get('designs/{design}', 'DesignsController@show')
        ->name('designs.show');  

And here is my Design Model:

    public function getRouteKeyName()
    {
        $slug = Str::slug('Laravel 5 Framework', '-');
        dd($slug);
        return 'designName';
    }

When I dd(slug); the output is 'Laravel 5 Framework' but I want it to be designName


Solution

  • Ok, so I am going to make some assumptions here, but lets say you have this function on your Design model:

    Design.php

    class Design extends Model
    {
        ...
    
        /**
         * Assuming you dont have a slug column on your designs table
         *
         * Also assuming the slug is built from a column called 'name' on
         * your designs table
         */
        public function getSlugAttribute()
        {
            return \Illuminate\Support\Str::slug($this->name);
        }
        
        // This assumes there is a column on posts table of 'design_id'
        public function posts()
            return $this->hasMany(Post::class);
        }
        ...
    }
    

    Now let's make an example of how you can build the desired route.

    EDIT

    Through further discussions with the asker, they wan't to show all Posts related to the Design they are showing (See model above). The setup in this answer is suitable for that, and you can refer to the show method defined below. Assume we have DesignsController.php:

    class DesignsController extends Controller
    {
        ...
        public function index()
        {
            return view('designs.index', [
                'designs' => Design::all(),
            ]);
        }
    
        public function show(Request $request, string $design)
        {
            // It is worth noting here if name has a double space you wont
            // be able to build backwards to it for a query
            // ie) Design\s\sOne !== $lower(Design\sOne)\
    
            $spaced = str_replace('-', ' ', $design);
            $lower = strtolower($spaced);
            $design = Design::with('posts')->whereRaw("LOWER(name) = '$lower'")->first();
            
            return view('designs.show', [
                'design' => $design,
            ]);
        }
        ...
    }
    

    Now in the 'designs/index.blade.php' file you could do something like:

        @foreach($designs as $design)
            <a href="{{ route('designs.show', [ 'design' => $design->slug ]) }}">{{ $design->name }}</a>
        @endforeach
    

    That would list all of your designs by name, linked to the designs.show route by their slug.

    If you would always like the slug value to be loaded when serializing to an array or json, you can add it to the protected $appends array on the model.

    If you don't ALWAYS want it appended, you need to append it at run time using for example $design->append('slug').

    Or if you have a collection of Designs you can do $designs->each->append('slug').

    Now in your designs.show blade file you can, access the Design's posts using the relation we loaded using Design::with('posts'), by doing the following:

        @foreach ($design->posts as $post)
            <img src="{{ asset('storage/'.$post->postImage) }}">
        @endforeach