Show Laravel resources API for only specific routes - laravel

Is it possible to show new added Laravel resources API fields on specific routes?
Example: I have a UserResource which has 3 fields - id, name and email. I'm returning UserResource in 10 routes. For 11th route added new field - posts.
Is it possible to show new posts field only for 11th route?

Of course you can, so you need to add two things. First inside of your controller in the method where you want to load posts do the following
return UserResourse::collection($users->with('posts'));
Then inside of your UserResourse class add the following
'posts' => PostResourse::collection($this->whenLoaded('posts'))
// or like this if you dont have a PostResouse
'posts' => $this->posts

Related

Custom Routes not working - 404 not found

I'm trying to create a custom route. The must be in this format: http://localhost:8000/home-back-to-school but instead I get a 404 not found error. http://localhost:8000/posts/home-back-to-school works, but that's not what I'm trying to get working.
My routes on web.php are defined as: Route::resource('posts',PostsController::class);
I modified the Route Service Provider by adding the code below:
parent::boot();
Route::bind('post',function($slug){
return Post::published()->where('slug',$slug)->first();
});
The published scope is defined in the Post Model file(Post.php) as:
public function scopePublished()
{
return $this->where('published_at','<=',today())->orderBy('published_at', 'desc');
}
I've done previously with laravel 5.x, now struggling with laravel 8.x
Link to the Documentation: Laravel 8 Documentation
You should define a custom route since you don't want to use the resourceful route for this method.
In your web.php
// Keep all your resource routes except the 'show' route since you want to customize it
Route::resource('posts', PostsController::class)->except(['show']);
// Define a custom route for the show controller method
Route::get('{slug}', PostsController::class)->name('posts.show');
In your PostController:
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
In your Post model:
// Tell Laravel your model key isn't the default ID anymore since you want to use the slug
public function getRouteKeyName()
{
return 'slug';
}
You may have to fix your other Post routes to make them work with this change since you are now using $post->slug instead of $post->id as the model key.
Read more about customizing the model key:
https://laravel.com/docs/8.x/routing#customizing-the-default-key-name
You should also remove the code you have in the boot method and use the controller instead.
Finally, make sure your post slug is always unique for obvious reason.
Note:
You may run into problems if your other routes are not related to the Post model.
Imagine if you have a route called example.com/contact-us. Laravel has no way to "guess" if this route should be sent to the PostController or the ContactController. contact-us could be a Post slug or it could be a static route to your contact page. That's why it's generally a good idea to start your urls with the model name. In your case, it would be a good idea for your Post route to start with "/posts/" like this: http://example.com/posts/your-post-slug. Otherwise you may run into all sorts unexpected routing issues.
Don't fight the framework: Always follow best practices and naming conventions when you can.

Laravel routing access category and show method

To display the blog list i have using the following route
// Blog List
Route::name('blog')->get('blog', 'Front\BlogController#index');
Ex: http://www.mypropstore.com/blog/
To display the blog category,
Route::name('category')->get('blog/{category}', 'Front\PostController#category');
Ex: http://www.mypropstore.com/blog/buy-sell
To display the blog details, comments and tag details, we have using "posts" middleware
// Posts and comments
Route::prefix('posts')->namespace('Front')->group(function () {
Route::name('posts.display')->get('{slug}', 'PostController#show');
Route::name('posts.tag')->get('tag/{tag}', 'PostController#tag');
Route::name('posts.search')->get('', 'PostController#search');
Route::name('posts.comments.store')->post('{post}/comments', 'CommentController#store');
Route::name('posts.comments.comments.store')->post('{post}/comments/{comment}/comments', 'CommentController#store');
Route::name('posts.comments')->get('{post}/comments/{page}', 'CommentController#comments');
});
Ex: http://www.mypropstore.com/posts/apartment-vs-villa-which-is-the-right-choice-for-you
Now i want to change the blog details url page to
http://www.mypropstore.com/blog/apartment-vs-villa-which-is-the-right-choice-for-you-{{blogid}}
Ex: http://www.mypropstore.com/blog/apartment-vs-villa-which-is-the-right-choice-for-you-54
If i change that above format, it conflict category page. Any body knows how to set the routing for blog details page(middleware "posts")
Assuming the blogid part, at the end of your suggested route...
http://www.mypropstore.com/blog/apartment-vs-villa-which-is-the-right-choice-for-you-{{blogid}}
...is numeric, you could do something like this:
For your route definition for your post details page, use the following:
Route::name('posts.display')
->get('blog/{slug}-{id}', 'PostController#show')
->where('id', '[0-9]+');
What this does is ensures that this route is only matched by paths that follow the pattern blog/{slug}-{id} but constrains that the id part of your route must be numeric i.e. consist only of one or more numbers.
You will need to ensure that this route appears before the one matching your category route or else the category route will take precedence.
Your controller should have a show method like this:
class PostController extends Controller
{
public function show($slug, $id)
{
// $id will contain the number at the end of the route
// $slug will contain the slug before the number (without the hyphen)
// You should be able to do this to get your post.
$post = Post::findOrFail($id);
dd($post);
}
}
Since your categories aren't numbers you could solve the conflict specifying that id will always be a number like this:
Route::get('/blog/{id}', 'BlogController#show')->where('id', '[0-9]+');

Want to show name instead of id in the URL field in Laravel

I don't want to show /route_name/{id} in the URL field of my Laravel project. Instead of that I want to show /route_name/{name} but pass the id in the back-end to the controller.
Suppose I have a route named departments and pass an id 3 named knee_pain as a parameter. And it is like /departments/3
But I want to to show /departments/knee_pain in my url and as well as want to pass the id 3 in my controller without showing the id in the url.
How to do that ?
In your model you can use the getRouteKeyName method to bind to another attribute than the default id in your routes :
public function getRouteKeyName()
{
return 'slug'; // Default is 'id'.
}
Rather than using the name attribute, that you could use elsewhere in your application for displaying the name of the entry, I recommend using an attribute made url friendly. You could use Str::slug() for that.
public function setNameAttribute($value) {
$this->name = $value;
$this->slug = \Str::slug($value);
}
It will 'slugify' your string, for example : \Str::slug('Knee pain') => 'knee-pain'.
Note : in Laravel 5.5, use the str_slug() helper.
You should also make sure this string is unique in your database.
First you have to garantee that the name is unique, if don't you will have more than one Id in your controller. For that i recommend you to use Purifier to remove spaces and make it URL friendly:
Purifier
Second, probably the best way to have clean controllers is creating a middleware that understand what kind of name is (what table should middleware look for). You can validate that by route name and send the correct id to controller.
Middleware docs

Pass parameter from route to controller in laravel?

I want to pass current agency id from route to controller in laravel 5.2.
ex:- I have one resource controller is AgencyController.
Route::resource('agencies', 'Admin\AgencyController');
I want to add one more route, such as,
Route::get('agencies/me', 'Admin\AgencyController#show', ['middleware' => ['web', 'agency']]);
Here I want to pass agency id from session as default parameter to agencyController#show function.
ex:- Auth::guard('agency')->user()->agencies_id
Is it possible in laravel?
Laravel routes function that if you configure your route like:
Route::get('user/{user}', 'UserController#someMethod');
you can pick up whatever you send after the slash in the controller. So if you call:
www.example.com/user/3
and your controller is like public function someMethod($id) the 3 will be forwarded to $id variable

routing forms and related models

This a beginners question about how to set up the route and controllers for a simple management system in Laravel 4.2.
Lets say I have a 'person' model (contains 'name' and 'email'). I also have a 'books' model that belongs to 'person' (contains 'title' and 'author', 'person_id').
To create a new person, the route to the form is:
example.com/persons/create
I would like the route for associating a new book to a person to be something like:
example.com/persons/22/books/create
Currently my routes are set up like this:
Route::resource('persons', 'PersonsController');
Route::resource('books', 'BooksController');
My person model contains the function:
public function books(){
return $this->hasMany('Book');
}
My book model contains the function:
public function persons(){
return $this->belongsTo('Person');
}
Where have I gone wrong? What have I missed?
From docs:
To "nest" resource controllers, use "dot" notation in your route declaration
Route::resource('persons.books', 'BooksController');
To get a list of registered route paths, you can use console command php artisan routes

Resources