how to hide id from url in Laravel 6? - laravel

hide id from url
https://wallpaperaccess.in/photo/162/download-wallpaper
i want url like this
https://wallpaperaccess.in/photo/download-wallpaper
ImagesController.php
public function show($id, $slug = null ) {
$response = Images::findOrFail($id);
$uri = $this->request->path();
if( str_slug( $response->title ) == '' ) {
$slugUrl = '';
} else {
$slugUrl = '/'.str_slug( $response->title );
}
$url_image = 'photo/'.$response->id.$slugUrl;
//<<<-- * Redirect the user real page * -->>>
$uriImage = $this->request->path();
$uriCanonical = $url_image;
if( $uriImage != $uriCanonical ) {
return redirect($uriCanonical);
}
Route
// Photo Details
Route::get('photo/{id}/{slug?}','ImagesController#show');
NOTE: i don't have any slug column in database, so can we use title as slug?

You should add a column field slug and auto-generate it from title
use Illuminate\Support\Str;
$slug = Str::slug($request->input('title'), '-');
In Models\Image.php
public function getRouteKeyName()
{
return 'slug';
}
In routes\web.php
Route::get('photo/{image:slug}','ImagesController#show');
In app\Http\Controllers\ImagesController.php
use app\Models\Image.php;
...
public function show(Image $image)
{
// controller will automatically find $image with slug in url
// $image_id = $image->id;
return view('your.view', ['image' => $image]);
}

In order to use a slug in the URL instead of an id, you'll need to...
Create a column in your table where you store the slug. A good way to make a slug unique is to append the actual id at the end. If you really don't want to see the id anywhere, you have no choice, you'll have to ensure the slug is unique yourself (there are a lot of ways to achieve this).
This is one way to automatically create an unique slug:
Make sure the slug column is nullable, then open your model and add these methods.
They are called "model events".
created is called when the model is, well, created.
updating is called when you are updating the model but before it's actually updated.
Using created and updating should automatically create the slug when you create or update a Images instance.
protected static function booted()
{
parent::booted();
static::created(function (Images $images) {
$images->slug = Str::slug($images->title.'-'.$images->id);
$images->save();
});
static::updating(function (Images $images) {
$images->slug = Str::slug($images->title.'-'.$images->id);
});
}
From a SEO point of view, updating the slug when the title change is arguably not a good practice, so you might want to omit this part (static::updating...), it's up to you.
Go to your model and add the following method:
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'slug'; //or whatever you call the slug column
}
This way, the router will resolve your model by the slug, not the id.
In your route file, remove the id and change the name of the slug to match the name of your model:
Route::get('photo/{images}','ImagesController#show'); //here I'm assuming your model is Images from what I see in your controller
In your controller, change the declaration of your show method to this:
public function show(Images $images)
{
dd($images);
// if you did all this correctly, $images should be the Images corresponding to the slug in the url.
// if you did something wrong, $images will be an empty Images instance
//
//
// your code...
}
Images should be renamed to Image, models should not be plural. However, it should not make any difference here.

Related

Laravel nested routes

I use laravel 7 and store following tree structures in database:
Catalogs table:
category1
--category11
--category12
----category121
----category122
--category13
Articles table:
news
--news1
--news2
Вasic Laravel routes looks like:
Route::get('category/{id}', 'categoryController#show');
Route::get('news/{id}', 'newsController#show');
But in this case "category" url's segment is necessary for each catalog's URL and
"news" url's segment is necessary for each new's URL
How can I route the following urls with Laravel Routes:
http://sitename.com/category1
http://sitename.com/category1/category11
http://sitename.com/category1/category12/category121
http://sitename.com/news
http://sitename.com/news/news1
?
You would need a “catch-all” route (registered after all of your other routes if you want all of the path to be configurable).
You could then explode the path on slashes, check each element is a valid category slug and is also a child of the previous category.
// All other routes...
Route::get('/{category_path}', 'CategoryController#show')->where('category_path', '.*');
You could also do this with a custom route binding:
Route::bind('category_path', function ($path) {
$slugs = explode('/', $path);
// Look up all categories and key by slug for easy look-up
$categories = Category::whereIn('slug', $slugs)->get()->keyBy('slug');
$parent = null;
foreach ($slugs as $slug) {
$category = $categories->get($slug);
// Category with slug does not exist
if (! $category) {
throw (new ModelNotFoundException)->setModel(Category::class);
}
// Check this category is child of previous category
if ($parent && $category->parent_id != $parent->getKey()) {
// Throw 404 if this category is not child of previous one
abort(404);
}
// Set $parent to this category for next iteration in loop
$parent = $category;
}
// All categories exist and are in correct hierarchy
// Return last category as route binding
return $category;
});
Your category controller would then receive the last category in the path:
class CategoryController extends Controller
{
public function show(Category $category)
{
// Given a URI like /clothing/shoes,
// $category would be the one with slug = shoes
}
}

What's the solution for adding unique slug to prevent duplicate entry in creating?

i'm working on a project for my homework which i'm trying to make it work as a spa, but I've got some problem in making slug for posts.
in the tutorial which i've followed, instructor used this to make a slug from title :
protected static function boot()
{
parent::boot();
static::creating(function ($course){
$course->slug = str_slug($course->name);
});
}
now, if i make this table unique, which this is what i wanna do. how should i prevent app from giving me duplicate entry? or how can i add something to slug, like a number, Every time i get duplicate entry?
if i make a post with This Post name twice, second time, i get duplicate Error.
In my opinion your selected answer is not getting close to something efficient. In large applications the 2 random strings can be overwritten in a short time and then you will have huge issues (code and DB).
A safer approach is to build a service and use that when you save the slug in the DB. Or course this is not 100% perfect but definitely is better then to increment 2 random strings. That, by the way, can also affect the SEO part of the app.
Below you can find my example:
The Model
public static function boot()
{
parent::boot();
static::saving(function ($model) {
$slug = new Slug();
$model->slug = $slug->createSlug($model->title);
});
}
The Service
<?php
namespace App\Services;
use App\Job;
class Slug
{
/**
* #param $title
* #param int $id
* #return string
* #throws \Exception
*/
public function createSlug($title, $id = 0)
{
// Normalize the title
$slug = str_slug($title);
// Get any that could possibly be related.
// This cuts the queries down by doing it once.
$allSlugs = $this->getRelatedSlugs($slug, $id);
// If we haven't used it before then we are all good.
if (!$allSlugs->contains('slug', $slug)) {
return $slug;
}
// Just append numbers like a savage until we find not used.
for ($i = 1; $i <= 100; $i++) {
$newSlug = $slug . '-' . $i;
if (!$allSlugs->contains('slug', $newSlug)) {
return $newSlug;
}
}
throw new \Exception('Can not create a unique slug');
}
protected function getRelatedSlugs($slug, $id = 0)
{
return Model::select('slug')->where('slug', 'like', $slug . '%')
->where('id', '<>', $id)
->get();
}
}
You could use inbuilt Str class, and create some random strings in your Post slug. Example:
static::creating(function ($course){
$course->slug = str_slug($course->name . Str::random( 2 ));
});
This will add 2 random strings on each slug you create, which will ensure there are no duplicates. You can find more about Str class here.
you can use laravel Inbuilt Helper, follow below link.
https://laravel.com/docs/5.8/helpers#method-str-slug
$data = 'My Data'
$slug = Str::slug($data, '-');
dd($slug);
add this in your controller
use Illuminate\Support\Str;

recover the slug of a category linked to another category Laravel

I would like to recover the slug of 2 categories from my routes but can’t write the Controller.
My Route
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
My Controller
public function viewoccupationcity($slug)
{
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
Route::get('technicians/o/{occupation}/c/{city}', 'User\TechnicianController#viewoccupationcity');
Your controller will accept the parameters from your route as variables by order
public function viewoccupationcity($ocupation, $city)
{
...
}
Example:
URL: technicians/o/foo/c/bar
public function viewoccupationcity($ocupation, $city)
{
// $ocupation will be 'foo'
// $city will be 'bar
}
Ok, you would need to retrieve 2 variables as that is what you are passing
public function viewoccupationcity($occupation, $city)
If you want the whole slug to do another search then you would use the $request object. So like so
public function viewoccupationcity(Request $request, $occupation, $city){ // You also need to include the Request decleration
$slug = $request->path();
$technicians = TechnicianResource::collection(occupation::where('slug',$slug)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
EDIT: We are having to do a lot of guesswork as your question isn't very clear. I think what you are trying to achieve is probably this
public function viewoccupationcity($occupation, $city){
$technicians = TechnicianResource::collection(occupation::where('city',$city)->where('occupation',$occupation)->firstOrFail()->technicians()
->with('city','occupation')
->latest()->get());
return $technicians;
}
If you need something more then you need to give more details

How can I change the URL in laravel

I have a blog list. When I click on a blog I want to make the URL as the blog name like : www.domain.com/blog/my-blog-name.
In my blog list I have the link as blog/blogid(2/3/4) ex : www.domain.com/blog/1
When someone visit www.domain.com/blog/1 then the URL should be www.domain.com/blog/my-blog-name { the blog name will change as per the blog Id }
I did so many research on this but no luck. It would be great if anyone help me to get out of this.
I am using laravel 5.4. I am new to laravel also.
You need to create a slug field in your database and you will find a post using slug instead of id
Define route:
Route::get('post/{slug}', 'PostsControler#findBySlug');
Example http://example.com/post/my-awesome-blog-post
and in your controller
//PostsController.php
public function findBySlug($slug)
{
$post = Post::where('slug', $slug)->firstOrFail();
// return view
}
I suggest you use Eloquent-Sluggable to auto create slug value from your title field
$post = new Post([
'title' => 'My Awesome Blog Post',
]);
$post->save();
// $post->slug is "my-awesome-blog-post"
You can create a unique slug or name for your blog , but since names can be duplicate and for the user it is difficult to manually create a unique slug, then I would advise you to do so
Route::get('/blog/{id}/{name}', 'BlogController#show');
this it will look nice in url, as here for example in stackoverflow!
You can just override the function getRouteKeyName
public function getRouteKeyName()
{
return 'slug';
}
Try with following one!
In your routes file:
Route::get('blog/{slug}', 'BlogController#show'); // make sure not conflict with resource routes define
And then your BlogController:
public function show($slug)
{
$id = explode($slug, '-')[0]; //get id of post
// your logic
}
Hope this helps you!
you can simply take your title from database and use it as a slug like below :
$slug = str_replace(' ','-',$yourtitle);
You can use Laravel mutators setSlugAttribute in your model:
public function setSlugAttribute($title){
$this->attributes['slug'] = str_replace(' ','-',$title);
}
and write this sample code in your web.php :
Route::get('/blog/{slug}', 'BlogController#show');
You can use slug in your method.
public function findBySlug($slug){
$post = Post::whereSlug($slug)->firstOrFail();
}

Retrieve parent class within morph relationship

I have this code
//ImageableTrait
trait ImageableTrait
{
public function images()
{
return $this->morphMany(Image::class, 'imageable')
->orderBy('order', 'ASC');
}
}
//User
class User extend Model
{
use ImageableTrait;
}
//Post
class Post extend Model
{
use ImageableTrait;
}
class ImageCollection extends Collection
{
public function firstOrDefault()
{
if ($this->count() === 0) {
$image = new Image();
$image->id = 'default';
$image->imageable_type = '/* I need the parent className here */';
$image->imageable_id = '.';
}
return $this->first();
}
}
//Image
class Image extend Model
{
public function imageable
{
return $this->morphTo();
}
public function newCollection(array $models = [])
{
return new ImageCollection($models);
}
public function path($size)
{
//Here, there is some logic to build the image path and it needs
//the imageable_type attribute no matter if there is
//an image record in the database or not
return;
}
}
I want to be able to do so
$path = User::find($id)->images->firstOrDefault()->path('large');
But I can't figure out how to get the parent class name to build the path properly...
I tried with $morphClass or getMorphClass() but can't figure out how to use it properly or if it is even the right way to do it.
Any thoughts on that?
I think you can keep it simple and drop the ImageCollection class because there is already a firstOrNew method that seems to be what you're looking for.
The firstOrNew method accepts an array of attributes that you want to match. If you don't care about the attributes, you can pass an empty array. If there are no matches in the database, it'll make a new instance with the proper parent type.
$path = User::find($id)->images()->firstOrNew([])->path('large');
Note: I am calling the images() method to get the MorphMany object so that I can call the firstOrNew method. In other words, you need to add the parentheses. Otherwise, you get a Collection.
Edit: If you want to make things a bit simpler by automatically setting some default attributes, you can add this to your ImageableTrait:
public function imagesOrDefault()
{
$defaultAttributes = ['id' => 'default'];
return $this->images()->firstOrNew($defaultAttributes);
}
Then, you can do something like this: $path = User::find($id)->imagesOrDefault()->path('large');
Note that your default attributes must be fillable for this to work. Also, imageable_id and imageable_type will automatically be set to your parent's id and type.
If you want to set the default value for imageable_id to a period and not the parent's id, then you have to alter it a bit, and it will look a lot like your original code except this will go inside your ImageableTrait.
public function imagesOrDefault()
{
// First only gets one image.
// If you want to get all images, then change it to get.
// But if you do that, change the conditional check to a count.
$image = $this->images()->first();
if (is_null($image))
{
$image = new Image();
$image->id = 'default';
$image->imageable_type = $this->getMorphClass();
$image->imageable_id = '.';
}
return $image;
}
Ok guys I've found something that seems to work pretty good for now so I'll stick with that.
In the Image model, I've added some code when I make the new collection:
public function newCollection(array $models = [])
{
$morphClass = '';
$parent = debug_backtrace(false, 2)[1];
if (isset($parent['function']) AND $parent['function'] === 'initRelation') {
if (isset($parent['args'][0][0])) {
$morphClass = get_class($parent['args'][0][0]);
}
}
return new ImageCollection($models, $morphClass);
}
I then simply retrieve the morphClass through the constructor of the ImageCollection
private $morphClass;
public function __construct($items = [], $morphClass)
{
parent::__construct($items);
$this->morphClass = $morphClass;
}
public function firstOrDefault()
{
if ($this->count() === 0) {
$image = new Image();
$image->id = 'default';
$image->imageable_type = $this->morphClass;
$image->imageable_id = '.';
}
return $this->first();
}
This way, I can simply call the method like that
User::with('images')->get()->images->firstOrDefault()
This seems to work really great in many cases, if I have some issues at some times, I'll update this post.
i may be late for the party, but i kinda did a small trick for morph relationships where i had "media" as morph, i get the parent since "model_type" has the full string parent class string.
$model = new $media->model_type;
$media->model = $model->findOrFail($media->model_id);

Resources