Route Parameters laravel issue - laravel

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'));
}
}

Related

Laravel Route with Multiple Parameters - How to get correct database result

I am a couple of weeks in to learning Laravel and have come across a problem which I can not find the answer to by myself, or online.
I am building a directory website with urls structured like:
directory.co.uk/parks
directory.co.uk/parks/{county-name}
directory.co.uk/parks/{county-name}/{park-name}
As {park-name} is not unique, I am struggling to return the page for an individual park. The controller needs to look up the county.id using the county.slug and then the park.id using the park.county_id and the park.slug.
I have routes in the web.php file such as:
Route::get('/parks','ParksController#index')->name('parks');
Route::get('/parks/{county}/{park}','ParksController#show')->name('park');
I have Parks and Counties models and (belongsTo and hasMany relationships set up between the two).
I have this is in both models:
public function getRouteKeyName()
{
return 'seo_url';
}
Then in my ParksController, I am at a loss. I currently have:
public function show(Counties $county, Parks $park)
{
//return $park;
//dd($park);
return view('parks.park', ['park'=>$park]);
}
I have also tried the non-Eloquent way:
public function show($county_slug,$park_slug)
{
$county = DB::table('counties')->where('seo_url',$county_slug)->get();
$county_id = $county->pluck('id');
$park = DB::table('parks')->where('county_id', $county_id)->where('seo_url', $park_slug)->get();
//dd($county_id);
//return $park;
return view('parks.park', ['park'=>$park]);
}
This returns a 404 error. Any help would be much appreciated. (I have done a lot of reading on Route model binding, but can not see any examples like mine.)
Laravel has an undocumented feature in its explicit model binding, where the callback can be given the current Route the binding is for. This can allow you to access the other parameters and use them to add conditionals.
Router::bind('park', static function ($value, Route $route) {
$query = Parks::where('seo_url', '=', $value);
if ($route->hasParameter('county')) {
$county = $route->parameter('county');
$query->where('county_id', '=', $county instanceof Counties ? $county->id : $county);
}
return $query->first() ?? abort(404);
});

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

How can add additional Attribute in my JSON response with dynamic value in laravel

In my app i have a Posts and a Reacts table both are connected with relationship.
In App user can react to a post(like or dislike) and for retrieve this i'm using this function :
public function feed()
{
$posts=Post::with('user')
->with('reacts')
->withCount('comments')
->orderBy('created_at', 'DESC')
->get();
return response()->json(["posts" => $posts]);
}
the response is:
i want to add one more field in Posts Object for isUserLiked and if the current authenticated user liked the post then value will be true or false for him something like this:
i can add a additional field but how can i set the value dynamically for that
this is what i am doing in my Post Model:
protected $appends = ['isUserLiked'];
public function getIsUserLikedAttribute($id)
{
$react=React::where('user_id',auth()->user()->id)->where('post_id',$id)->exists();
return $react;
}
this is returning false because i don't know any way to pass the arguments(Post id).
is there any better way i can get the desired response? Thanks!
public function getIsUserLikedAttribute($id)
{
return React::where('user_id',auth()->user()->id)->where('post_id',$this->id)->exists();
}
In your user model:
public function reacts(){
return $this->hasMany(React::class);
}
public function scopeReactOnPost($query, $post_id){
return $this->reacts()->where(function($query) use ($post_id){
$query->where('post_id',$post_id);
});
}
and in your controller:
$user->reactOnPost($post_id)->first();
or
$user->reactOnPost($post_id)->get()->count();
Will let you know if user had any reaction on the specified post.
and for adding this to your json output you can artisan make a resource for your post model. Laravel Resources

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

Nested Eager loading relationships in Laravel

I am creating a forum software using Laravel and I'm trying to access the most recent posts made within a specific topic using latestPost as defined in my model below
public function latestPost() {
return $this->hasOne('App\Post')->with('thread')->with('author')->latest();
}
Here is how the nested relationships are defined in App\Post.php
public function author()
{
return $this->belongsTo('App\User', 'author_id');
}
public function thread()
{
return $this->belongsTo('App\Thread', 'thread_id');
}
public function topic() {
return $this->belongsTo('App\Topic');
}
I have been able to access posts of threads, authors etc no problem, and when I have {{ $topic->latestPost }} in my views, it will list the object including relationships successfully. But as soon as I try to display a specific part such as {{ $topic->latestPost->author->username }} I am getting the following error: Trying to get property of non-object
In this specific view, topics is yet another relationship pulled from Categories like so in the Controller and getting $topics using a #foreach on $categories->topics:
public function index() {
$categories = Category::orderBy('order', 'asc')->get();
return view('welcome', compact('categories'));
}
Here is a dump of $categories: http://dumptext.com/H8Nq16ea
Maybe you want to use nested eager loading.
Change: return $this->hasOne('App\Post')->with('thread')->with('author')->latest();
To this: return $this->hasOne('App\Post')->with('thread.author')->latest();
Also i never heard about latest() and the end of your return statement. Maybe you can try it with orderBy as shown below.
`return $this->hasOne('App\Post')->with('thread.author')->orderBy('id')->first;`
I guess you can use the orderBy('id')->reverse() function if the result is not in the order you want. Let me know if it helped you out.
You should make sure you have author for latest post you want to display. Otherwise if you expect not to have one for each record, you should try:
{{ $topic->latestPost->author ? $topic->latestPost->author->username : 'No author' }}
In addition instead of:
return $this->hasOne('App\Post')->with('thread')->with('author')->latest();
you can use
return $this->hasOne('App\Post')->with('thread','author')->latest();

Resources