Resolving a model using multiple columns in a Laravel route - laravel

I'm trying to register a route to fetch a single model automatically based on multiple columns. For example:
Route:get( 'sample/{myModel:status}/{myModel:slug}', [ SomeController:class, 'show' );
Let's access this URI:
www.example.com/sample/publish/this-is-a-slug
Now what I expect to get in my controller is a single myModel instance, which has a status column equal to publish and a slug column equal to this-is-a-slug. Is this possible?
Trying to register the above route will result in a reference error:
Route pattern "/sample/{myModel}/{myModel}" cannot reference variable name "myModel" more than once.
Now I can just drop the model name and do the query in my controller, but I wonder if it's possible to inject a model into a controller using more than 1 column. The slug column is not unique, but the combination of slug and status is.

You may define the route like this,
Route::get('sample/{model_by_status}/{slug}', [ SomeController:class, 'show'] );
Then you can add route binding in the boot() method of RouteServiceProvider.php like this,
Route::bind('model_by_status', function($value){
$slug = request()->route('slug');
return myModel::where([
'status' => $value,
'slug' => $slug,
])->firstOrFail();
});
And in your controller method you need to declare the show method like this
public function show(myModel $model_by_status){
}
Note that the naming is important here. model_by_status (whatever in your case) must be same in all those three places

Related

Laravel controller return model instance instead data content

i have this route:
Route::group(['prefix' => 'comments', 'middleware' => ['auth:api']], function(){
Route::delete('/delete/{id}', [CommentController::class, 'destroy'])->name('comment.delete');
});
in my controller i have
public function destroy(Comments $comments)
{
dd($comments);
}
normally i must have the data of respective of the id right? but instead i got empty instance of the model
To work you need to change route parameter from id to comments
Laravel automatically resolves Eloquent models defined in routes or
controller actions whose type-hinted variable names match a route
segment name.
Route::delete('/delete/{comments}', [CommentController::class, 'destroy'])->name('comment.delete');
Read here : https://laravel.com/docs/8.x/routing#implicit-binding.

My form requires additional data, where should I put my routes and controller methods to get it?

I'm learning how to create web application with laravel and I don't know correct way to create struct for this example form:
I have simple controller named i.e. PostController with methods:
index(), create(), store(), show(), edit(), update(), destroy()
created via make:controller --resource.
And I have routes for it in my web.php file created via Route::resource() method.
Now, my create() method in PostController display a form for create new post, but this form required select an option from list.
I can't display whole list because it's thousands of options do I want create search field which use ajax to get available options from my database. Then user just select an option and can send form.
It's ofcourse just example but in that situation where should I put my method controller and route for search options?
Should I crate additional method in my PostController ( i.e. searchOption( $string ) ) or should it be new controller?
If it should be new controller then do you have any suggestion for folder/name structure for it?
And where should I put my routes for it? Still in web.php? or api.php?
And again, how should I name this route?
Sinse you have another entity you can make another controller. Let's say you want filtered City in your Post's create view.
// Controllers/Api/CityController.php
public function index(Request $request)
{
$query = City::query();
if ($request->has('search')) {
$query->where('name', 'LIKE', $request->input('search') . '%');
}
$cities = $query->paginate();
return response()->json(['cities' => $cities]);
}

Laravel Nova Custom Fields get model value

I created a custom field for Nova.
On this field I need to get some extra model data like $model->country to show on the form.
How can I pass this data to the Vue component?
I try to use:
return $this->withMeta
But I don't know how to pass the data from the model.
Add the custom field like a normal field to the resource fields() and chain a custom method with the data from the model:
CountryField::make('Country')->country('Germany'),
Define this custom method in your Nova component (see src folder):
public function country($value)
{
return $this->withMeta([
'country' => $value,
]);
}
You can access the returned data from this method in FormField.vue like this:
{{ field.country }}

changing key names when leveraging laravel resources to return API data

So I am currently building out an API and see that laravel has added an API resource which I am assuming is in lieu of something like fractal?
However running into an issue where when I go to return a collection using the XyzCollection resource I do not have the ability to change my DB names? So i end up with the same naming convention I have in my DB..
Plenty of obvious reasons why I don't want to have that be the case (i can go into them if need be) HOWEVER - is there a way to alter those key names?
for example in one of my tables I have a id,uuid,user_uuid
Now in fractal I would just transform these fields like so -
'id' => $xyz->uuid,
'user' => [
'data' => [
[
'user_id' => $xyz->user->uuid,
'username' => $xyz->user->username,
]
]
],
How do i do this when all i can do is pass in the collectionin the XyzCollection?
'data' => $this->collection,
and before you say use XyzResource ... When I am returning all(); records like I need to do plenty of times in an API (or paginate) etc etc. I can only do that from XyzCollection!
Thanks in advance
Steve
You first need to define a structure for a singular JsonResource object:
(php artisan make:resource Xyz)
public function toArray($request)
{
return [
'user_id' => $this->uuid,
'username' => $this->username,
];
}
And then tell your XyzCollection to use the class:
Customizing The Underlying Resource Class
Typically, the
$this->collection property of a resource collection is automatically
populated with the result of mapping each item of the collection to
its singular resource class. The singular resource class is assumed to
be the collection's class name without the trailing Collection
string.
For example, UserCollection will attempt to map the given user
instances into the User resource. To customize this behavior, you may
override the $collects property of your resource collection
(From https://laravel.com/docs/5.7/eloquent-resources#concept-overview)
If your ResourceCollection doesn't do anything extra, you might not always need it. Every JsonResource can transform itself into a resource collection with the collection() method, e.g. UserResource::collection($users)

How to create slugs in Laravel?

I'm trying to develop an e-com website in Laravel 5 (Since it required lot of customization, I decided not to use Magento or OpenCart).
The problem I'm facing is to creating category and product URLs from slugs.
For example, one URL can be:
http://somewebsite.com/products/lenovo-yoga-500
But I want to make URL like:
http://somewebsite.com/lenovo-yoga-500
I tried to create a route like:
Route::get('{slug}', 'BaseController#route');
and in the route() method, I tried:
function route($route){
$product = Product::where(array('slug' => $slug))->first();
if(isset($product))
.....
else
.....
but first of all, the value is not getting passed to variable '$slug' and secondly I want to redirect the request to another route after checking the condition.
You might want to go with the first URL (product/{slug}) because the way you have it setup with the slug as the first parameter, every route you make for any page will be interpreted as the slug.
Either way, fix your controller method as such
function route($slug){
$product = Product::where(array('slug' => $slug))->first();
if(isset($product))
.....
else
.....
The documentation covers everything you are asking for here, so please read through about redirecting to routes.
To achieve it like Magento does, you need to add a new product-routes.php file that will be rebuilt.
First instruct app/Providers/RouteServiceProvider.php to load your dynamic route file thus:
protected function mapWebRoutes(Router $router)
{
$router->group([
'namespace' => $this->namespace,
], function ($router) {
require app_path('Http/routes.php');
require base_path('resources/routes/product-routes.php');
});
}
Next: Rebuild product-routes.php automatically on save or when the user requests it in the backend to include all product slug routes and to load the product by slug.
Hope that helps.
in Product-model:
public function getRouteKeyName() {
return 'slug';
}
in routes:
Route::get('{product}', 'ProductController#product');
in ProductController:
public function product(Product $product) {
$product->doSomething();
}
Note that the route should be last in the routes-file so any other route has chance to match before that.
You must use this Route
Route::get('/sample/show/{sample : slug}',[SampleController::class]);
Enter the model of the controller and set the value of getRouteKeyName equal to slug such as:
public function getRouteKeyName(){return 'slug';}
Custom Keys & Scoping
When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user:
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { return $post;});

Resources