Reputation: 1
I am building a small app in Laravel, but I needed to use some help. I read similar tasks but I don't see them related to what I have.
The app that I'm building is a dictionary and I really don't get the part where words are displayed by id in the URL. I tried to revert the logic for the curds (store, update, delete, show) so that they perform actions using the word name and not the word id. That worked. The redirecting to route app/{name} after the actions does work too.
Now, I presume this is not how it needs to be done, when the curds aren't taking actions on the id?
I also read about slugs, but since I'm building a dictionary, don't understand why I need to separate the word by a "-"? It's a single word, not like posts. And why do I need to have an extra field in my form for the slug, as the slug must be equal to the name? I'm getting here an error: the slug->unique() field must be not null.
If somebody has the time, please explain to me what's the best way for making the names of the words being displayed in the URL. Thank you, I appreciate it.
Edit
Here is my code for the update crud:
public function update(UpdateWordRequest $request, $name)
{
Word::query()->where('name', $name)->firstOrFail()->update($request->input());
return redirect()->route('dictionary.show', ['name' => $name])->with('success', 'Edited successfully!');
}
It's working fine, but when I re-edit the word and change it to word2, with a number, I then get this error: Trying to get property 'name' of non-object.
What do I need to make of it? Thank you :)!
Upvotes: 0
Views: 551
Reputation: 1
Here is what I finally come up with.
Word.php
I used as @usrNotFound suggested spatie/laravel-sluggable.
<?php
namespace App;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
class Word extends Model
{
use HasSlug;
/**
* @return SlugOptions
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug')
->slugsShouldBeNoLongerThan(20);
}
/**
* @param $query
* @param $slug
* @return mixed
*/
public function scopeFindBySlug($query, $slug)
{
return $query->where('slug', $slug)
->get();
}
}
WordController.php
<?php
namespace App\Http\Controllers;
use App\Word;
use App\Http\Requests\CreateWordRequest;
use App\Http\Requests\UpdateWordRequest;
class WordController extends Controller
{
/**
* Store a newly created resource in storage.
* @param CreateWordRequest $request
* @return mixed
*/
public function store(CreateWordRequest $request)
{
$word = Word::query()
->create($request->input());
return redirect()
->route('dictionary.show', $word->slug)
->with('success', 'Created successfully!');
}
/**
* Display the specified resource.
* @param $slug
* @return mixed
*/
public function show($slug)
{
$word = Word::whereSlug($slug)
->firstOrFail();
return view('word.show')
->with('word', $word);
}
/**
* Update the specified resource in storage.
* @param UpdateWordRequest $request
* @param $slug
* @return mixed
*/
public function update(UpdateWordRequest $request, $slug)
{
Word::whereSlug($slug)
->firstOrFail()
->update($request->input());
return redirect()
->route('dictionary.show', $slug)
->with('success', 'Edited successfylly!');
}
/**
* Remove the specified resource from storage.
* @param $slug
* @return mixed
*/
public function destroy($slug)
{
Word::whereSlug($slug)
->delete();
return redirect()
->route('dictionary.index')
->with('success', 'Deleted successfully');
}
}
Route.php
Route::get('/dictionary/{slug}', 'WordController@show')
->name('dictionary.show')
->where('slug', '[-A-Za-z0-9_-]+');
Creating, updating, destroying new words works fine. Eventually there is a small problem. When I want to update a particular word and change the name of it, after saving the word, I'm getting
Sorry, the page you are looking for could not be found.
and if I look at the url I see the old name instead of the new one. Has this to do with my update method and how do I fix it?
Upvotes: 0
Reputation: 2800
I think you should be able to use a trait to fix this issue.
I would create a trait call hasSlug and have the following code:
<?php
namespace App\Traits;
trait HasSlug
{
protected static function bootHasSlug()
{
static::creating(function ($model) {
$columnName = static::getSlugColumn();
$model->$columnName = str_slug($model->name); //convert your name to slug
});
}
public function getSlugAttribute()
{
$columnName = static::getSlugColumn();
return (string) $this->attributes[$columnName];
}
protected static function getSlugColumn()
{
if (isset(static::$slugColumn)) {
return static::$slugColumn;
}
return 'slug';
}
}
So now if you use HasSlug trait to your model every time you create a record it will go and look for name field from your model and create a slug out of it.
Be sure to apply unique constrain to your name field if needed.
For more robust have a look at spatie slug package https://github.com/spatie/laravel-sluggable
Upvotes: 3