Laravel 5.4 - route model binding condition - laravel

I have implemented route model binding as follows:
Route:
Route::get('property/{property}', 'PropertyController#view');
Controller:
public function view(Property $property)
{
$data = compact([
'property'
]);
return view('property.view', $data);
}
This works great. However I want to add a condition to the Property model to check that active = 1. How and where do I do this?

You can register an explicit binding. Add the code below to RouteServiceProvider. This will be applied to the model binding when the segment is property.
Route::bind('property', function ($id) {
return \App\Property::where('id', $id)
->where('active', 1)
->firstOrFail();
});
If you need this condition to be applied globally for every result then you can add a global scope instead. https://laravel.com/docs/5.4/eloquent#global-scopes

Related

Laravel - how to bind soft deleted route and model

I'm using soft delete in model Article, but in model Comment not use soft delete. I'm also customize the key using slug column in model Article. If the article is deleted, I want still show the comment. But when article is deleted, show method always return 404.
public function show(Article $article, Comment $comment)
{
if ($article->id != $comment->article_id)
throw new NotFoundHttpException('Record Not Found.');
return $this->success(['comment => $comment']);
}
How to fix this?
Your question statement is not defining the problem you should ask how to bind soft deleted route and model.
Laravel provide ->withTrashed() method for this so it also bind soft deleted models in route.
web.php
user App/Http/Controller/ArticleController;
Route::get('article/{article}', [ArticleController::class, 'show'])->name('article.show')->withTrashed();
But this method added in Laravel 8.55 If you have older version so you can simply find model in controller without route model binding.
ArticleController.php
public function show($article, App/Comment $comment)
{
$article = App/Article::withTrashed()->findOrFail($article);
if ($article->id != $comment->article_id) {
throw new NotFoundHttpException('Record Not Found.');
}
return $this->success(['comment => $comment']);
}
Or you can also use Explicit Binding for specific model in RouteServiceProvider.
public function boot()
{
parent::boot();
Route::bind('article', function ($id) {
return App\Article::withTrashed()->find($id) ?? abort(404);
});
}
And you can also use onlyTrashed() method in explicit binding in case you use separate route for trashed models.
If you want to get deleted records as well, use the method withTrashed
Your code should look something like this:
Article::withTrashed()->find($id);
Hope it help u and happy coding !

How to use custom model function in eloquent

I want to get all user's online friends, how can I call a custom model function inside the eloquent condition?
this is my code
$friends = $user->friends()->where(function (Builder $query){
$query->where('friend', 'yes');
})
->get();
and this is my function in model
public function getIsOnlineAttribute(): bool
{
// check if the user is online or not
return $this->is_online;
}
I can access is_online after eloquent by foreach, but in my case, I want to check everything in one step ( inside where condition in eloquent). how can I do that???
You can't use conditions for eloquent accessors, in this case you can use (assume 1 is database column value):
$friends = $user->friends()->where('is_online', 1)->get();
or
$friends = $user->friends()->whereIsOnline(1)->get();
or you can create eloquent scope on your model:
public function scopeIsOnline($query) {
$query->where('is_online',1);
}
and you can use this eloquent scope on your controller in this way:
$friends = $user->friends()->isOnline()->get();
this worked for me :)
$friends = $user->friends()
->simplePaginate()
->reject(function ($friend) {
return $friend->is_online === false;
});

laravel 5.6 Eloquent : eloquent relationship model creation issue

in my controller i create an Eloquent Model Instance passign throug a relation. The model is loaded on controller's __construct, that's why is present a $this->store and not a $store.
public function index()
{
if (is_null($this->store->gallery)) {
$this->store->gallery()->create([
'title' => 'gallery_title,
'description' => 'gallery_description',
]);
}
$gallery = $this->store->gallery;
dd($gallery);
return view('modules.galleries.index', compact('gallery'));
}
Simply if a store's gallery is not present yet, let's create it.
The first time i print out my dd() is ALWAYS null, if i reload the page the dd() show correctly my gallery model.
The things is weird for me, seems like the first time the creation is done but not ready... I can work around but why this code doesn't work the first time?
Help is very appreciate.
Relationship codes: on gallery ....
public function store()
{
return $this->belongsTo(Store::class);
}
on store...
public function gallery()
{
return $this->hasOne(Gallery::class);
}
When using the $this->store->gallery()->create() method, the original method is not hydrated with the new value, you can simply do a
$gallery = $this->store->refresh()->gallery;
OR
$gallery = $this->store->load('gallery')->gallery;
if you want to make your code cleanner you can do that in your Store Model:
public function addGallery($gallery){
$this->gallery()->create($gallery);
return $this->load('gallery')->gallery;
}
And that in your controller:
$gallery = $this->store->addGallery([
'title' => 'gallery_title',
'description' => 'gallery_description',
]);
and voila ! You have your gallery ready to be used :)
It's the lazy load part of Eloquent. basicly, when you tested for it with is_null($this->store->gallery) it sets it to that value.
when you tried to recover it again, it did not do the DB query, it just loaded the value already present (null).
after creation you need to force reload the relation:
$this->store->load('gallery');
or
unset($this->store->gallery);
or
$gallery = $this->store->gallery()->get();

how to send variable from controller to model function in laravel

I want to send variable $typeid to function categories to use it in query is there a way Knowing that when I try to use new instance of class in my controller like that:
$cat= new Main_category();
$categories = $cat->categories()->get();
it returns empty array
the following code is working well when I manually add the typeid inside the model function I want to have it as a variable sent from controller
controller:
$categories = Main_category::with('categories')->get();
Model
public function categories()//($typeid)
{
$query = $this->hasMany(Category::class, 'main_cat_id')
->join('category_type','category_type.cat_id','=', 'categories.cat_id')
->join('main_categories','main_categories.main_cat_id','=', 'categories.main_cat_id')
->where('category_type.type_id', '1'); // I want to use $typeid here
return $query;
}
I am not sure whether you can pass your variable in eloquent relationship methods by using with method or not. But you can add a where clause in controller.
Main_category::with(['categories' => function($query) use($typeid) {
$query->where('category_type.type_id', $typeid);
}])->get();
Or you can create a query scope for model too.
in Model
public function scopeWithCategories($query, $typeid) {
return $query->with(['categories' => function($query) use($typeid) {
$query->where('category_type.type_id', $typeid);
}]);
}
and finally in Controller
Main_category::withCategories($typeid)->get();

Route Parameters laravel issue

I simply made this but /maps/{category}/{map} does not work correctly.
putting anything on {category} showing same result.
/maps/php/1
/maps/laravel/1
I want to show a result when category's name and map's id exactly matches otherwise redirect to homepage.
My route
Route::get('/maps/{category}', 'MapsController#index');
Route::get('/maps/{category}/{map}', 'MapsController#show');
My controller
public function show(Category $category, Map $map)
{
return view('maps.show', compact('map'));
}
My blade template
{{ $map->title }}
Swap over the routes in your routes file so that the more specific one is first.
Route::get('/maps/{category}/{map}', 'MapsController#show');
Route::get('/maps/{category}', 'MapsController#index');
Firstly, there is no need to swap order of routes if you are using 5.4, since I started with 5.4 to use laravel i cannot say anything for previous versions.
If you you want to filter asked map if it matches given category, you can use "whereHas" method with eloquent if you defined relationship between category and map.
"One To Many (Inverse)" relationship is what you need to use, check here: https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse
And querying relationship is what you need to know, check here: https://laravel.com/docs/5.4/eloquent-relationships#querying-relationship-existence
How your relationship should look like in Map model:
/**
* Get the category that owns the map.
*/
public function category()
{
return $this->belongsTo('App\Category');
}
An example code:
public function show($category, $map)
{
$map = Map::whereHas('category',function($query) use($category){
$query->where('id', $category);
});
return view('maps.show', compact('map'));
}
Route::get('/maps/{category}', 'MapsController#index');
is a more generalized route so it matches more than
Route::get('/maps/{category}/{map}', 'MapsController#show');
You should list the show route first.
To match an exact model attribute (i.e. name), you should customize the resolution logic the router uses in the route service provider boot method. For example:
Route::bind('category', function ($value) {
return App\Category::where('name', $value)->first();
});
Route::bind('map', function ($value) {
return App\Map::where('id', $value)->first();
});
public function show($category,$map)
{
$category = App\Category::where('category_name',$category);
$map_id =App\Map::where('map_id',$map);
if(!empty($category) && !empty($map_id))
{
return view('maps.show', compact('map'));
}
else
{
return view('homepage', compact('map'));
}
}

Resources