Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

henryoladj's avatar

Generate Clean SEO URL

My URL is currently like this -> http://localhost:8000/news?tags=1. 1 is ofcourse the ID of the tags in the database

i have tags with names (Entertainment, News) How can i make my URL Look like this lhttp://localhost:8000/entertainment instead of http://localhost:8000/news?tags=1

NewsController.php

<?php

namespace App\Http\Controllers;

use App\News;
use App\Tag;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class NewsController extends Controller
{
    function __construct()
    {
        $this->middleware('auth', ['except' => ['index', 'show']]);
    }
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {   
        if($request->has('tags')){
            $tag=Tag::find($request->tags);
            $news=$tag->news;
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact('news'));
    }

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

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //validate

        $this->validate($request,[
            'subject'=>'required|min:10',
            'tags' => 'required',
            'body' => 'required|min:20'

        ]);
        
        //store
        $news=auth()->user()->news()->create($request->all());

        $news->tags()->attach($request->tags);

        //redirect
        return redirect()->route('news.index');
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\News  $news
     * @return \Illuminate\Http\Response
     */
    public function show(News $news)
    {   
        
        
        return view('news.single', compact('news'));
    }

    

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\News  $news
     * @return \Illuminate\Http\Response
     */
    public function edit(News $news)
    {
        return view('news.edit', compact('news'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\News  $news
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, News $news)
    {
        if(auth()->user()->id !== $news->user_id){
            abort(401, "Please Login");
        }

        $this->validate($request,[
            'subject'=>'required|min:10',
            'body' => 'required|min:20'
        ]);

        $news->update($request->all());

        return redirect()->route('news.show', $news->slug)->withMessage('News Updated');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\News  $news
     * @return \Illuminate\Http\Response
     */
    public function destroy(News $news)
    {   
        if(auth()->user()->id !== $news->user_id){
            abort(401, "Please Login");
        }

      
        $news->delete();
        $news= News::paginate(15);
        return view('categories.news', compact('news'));
    }
}

Present URL

<a href="{{route('news.index', ['tags'=>$tag->id])}}">{{$tag->name}}</a>

ROUTES

Route::resource('/news', 'NewsController');


Route::get('/post/{news}', 'NewsController@show')->name('news.show');
0 likes
36 replies
jlrdw's avatar

Well you can do that with using route parameters.

But you still have pagination as query string:

somesite.com/whatever/something?page=2

SEO has nothing to do with query string, what you refer to is just "friendly" url.

Sti3bas's avatar

Routes:

Route::get('/{tag?}', 'NewsController@index')->name('news.index');

Controller:

public function index(Request $request, $tag = null)
{   
    if($tag){
        $news = Tag::where('name', $tag)->firstOrFail()->news;
    }else{
        $news = News::paginate(15);
    }
    return view('categories.news', compact('news'));
}

Link:

<a href="{{route('news.index', ['tag' => $tag])}}">{{$tag->name}}</a>
Snapey's avatar

Controller

        if($request->has('tags')){
            $tag=Tag::where('name',$request->tags)->first();
            $news=$tag->news;
        }else{
            $news= News::paginate(15);
        }

searching for the name of the tag rather than the ID

Then change the link to send the name rather than the ID

jlrdw's avatar

@snapey this has come up before, in the route:

Route::get('/{tag?}', 'NewsController@index');

How do you initially get the {tag?} in there.

Meaning, many times the initial search is a post. After that, during paginating you use a get route.

Me, I just get the POST values in the request and build a redirect to the get route with the parameters. Works perfect.

    public function processAdmin()
    {
        if (!ChkAuth::chkRole('admin')) {   // ignore I have custom Auth
            return redirect('indexbl');
        }
        if(Request::has('submit')){
            $dogsearch = !empty(Request::input('psch')) ? Request::input('psch') : '';
            return redirect('dog/indexadmin/' . $dogsearch);
        }
    }

You have to somehow initially have the parameter to use.

Gives

somesite.com/dog/indexadmin/c?page=2

If "c" was the search string. But paginator is still query string.

Just saying many do not take into account an initial search for products as example is a post, not a get.

jlrdw's avatar

I don't use tags. If I am searching for something or whatever I use field names.

$dogsch = $dogsearch . "%";

$query = Dog::where('dogname', 'like', $dogsch);
        if ($aval == "n") {
            $query->where('adopted', '=', 1);
        } else if ($aval == "y") {
            $query->where('adopted', '=', 0);
        }
        $dogs = $query->orderBy('lastedit', 'DESC')->paginate(5);

        $params = array('aval' => $aval);

Probably what snapey said for tags. I was just saying a search usually comes from a posted form, those parameters have to be converted to usable route parameters, route parameters don't just get there from nowhere.

Adopted above is a field name, dogname is a field name.

Edit: I wouldn't know how to do a search on a "tag".

But if speaking of friendly url, pagination and searching has to be taken into account. Most crud is derived from a search to start with.

jlrdw's avatar

Like I said use snapey answer, I know I over explain sometimes I was just trying to help you for future.

Just trying to say a get route has to be built up correctly.

I guess when I saw ?tags=1, the query string I figured you were searching.

Snapey's avatar

ok, you want to accept as route parameter rather than query string, and you also want it to work for just /news/ (unfiltered)

web.php


Route::get('news/{tag?}', 'NewsController@index')->name('news.index');

Controller

    public function index(Request $request, $tag=null)
    {   
        if($tag)){
            $tag=Tag::find($tag);
            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }

url

<a href="{{route('news.index', ['tag'=>$tag->name])}}">{{$tag->name}}</a>

I think thats it.

Main thing is optional tag in the URL and optional tag in the controller method

You also need to paginate on both paths so that the view can rely on getting paginated posts

I've also added the $tag into the view so that you can show what the current tag is

henryoladj's avatar

@snapey with this controller

public function index(Request $request, $tag=null)
    {   
        if($tag)){
            $tag=Tag::find($tag);
            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }

I am getting error syntax error, unexpected ')'

But after trying this

public function index(Request $request, $tag=null)
    {   
        if($tag){
            $tag=Tag::find($tag);
            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }

it takes me to http://localhost:8000/news/news i get 404 not found, the posts aren't there

jlrdw's avatar

Build the initial URL you need in a controller on first use only.

Redirect it to the route you have built that's exactly like you need.

Everything should now work perfect, either use a single controller and Skip if not first use or use two separate controllers choice is yours.

But somehow you have to initially build the URL you expect to match a route.

I've already given an example of a redirect.

Those route parameters have to come from somewhere they don't just get there by themselves.

No idea how are you are doing your very first page load.

Edit you have

    if($request->has('tags')){

How are you initially getting parameters in the request. You said you had no form you are submitting.

Snapey's avatar

Sorry, the last example I posted reverted the earlier change. It should be looking for the name not using find

public function index(Request $request, $tag=null)
    {   
        if($tag){
            $tag=Tag::where('name',$tag)->first();
            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }
henryoladj's avatar

@snapey So

Controller Would be like this

public function index(Request $request, $tag=null)
    {   
        if($tag){
            $tag=Tag::where('name',$tag)->first();
            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }

web.php

Route::get('news/{tag?}', 'NewsController@index')->name('news.index');

link

<a href="{{route('news.index', ['tag'=>$tag->name])}}">{{$tag->name}}</a>

after trying this, the link is now http://localhost:8000/news/entertainment but i am getting 404 | Not Found no post under that tags appears

Snapey's avatar

What do you get with dd() added as shown

public function index(Request $request, $tag=null)
    {   
        if($tag){
            $tag=Tag::where('name',$tag)->first();

            dd($tag);

            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }
Snapey's avatar

Is your tag named Entertainment or entertainment You have to use the same lettercase

henryoladj's avatar

@snapey the name is entertainment in my tags table in database

and this code below Still gives 404 on http://localhost:8000/news/entertainment

public function index(Request $request, $tag=null)
    {   
        if($tag){
            $tag=Tag::where('name',$tag)->first();

            dd($tag);

            $news=$tag->news()->paginate(15);
        }else{
            $news= News::paginate(15);
        }
        return view('categories.news',compact(['news','tag']));
    }
Snapey's avatar

So its not hitting this code at all?

Do you have other /news routes in your web.php?

henryoladj's avatar

@snapey it is working now

initially i had another /news in my route which is

Route::resource('/news', 'NewsController');

But i have removed it and the pages load with respective posts

henryoladj's avatar

@snapey But news.create won't work after removing it if i want to create new posts

<div class="new-topic">
            <a href="{{route('news.create')}}">( Create New Topic )</a>
        </div>

Saying Route [news.create] not defined.

Snapey's avatar
Snapey
Best Answer
Level 122

ok, so its not possible to share the same url

You could leave /news/ as a resource controller and come up with a new route for your index, for instance

Route::get('news/tagged/{tag?}', 'NewsController@index')->name('news.index');

put this route before your resource route

Snapey's avatar

isn't this a new question?

You did not load the tag model when you fetched the news?

henryoladj's avatar

@snapey Post i make under the entertainment tag appears twice. How do i take care of this bug?

Snapey's avatar

You sure its only in the database once?

Next

Please or to participate in this conversation.