Laravel eloquent undefined method user when querying 3 tables - php

Hi I'm trying to query three tables from my client controller, a quick overview of my database, users clients projects tasks a user hasMany clients, projects and tasks and these projects and tasks also belongTo a client.
So I'm in the Client Controller and I want to query the logged in users clients projects, however when I try to do this I get thrown an undefined method error:
BadMethodCallException Call to undefined method Illuminate\Database\Query\Builder::user()
I'm not sure why this is occurring, I queried the clients projects separately and it works fine but when I add an additional layer it throws me the above error.
I'm a newbie on Laravel 4 so would appreciate some guidance to help rectify the error and help me understand where I'm going wrong.
My code is below:
ClientController.php
public function show($id)
{
$client = Client::find($id);
$client->load(array('projects' => function($query)
{
// With the clients for each project
$query->with('user');
}));
// Create an empty array
$associated = array();
// Loop through client projects
foreach($client->projects as $project):
// Loop through project users
foreach($project->user as $user):
// Check if the user is the same as the logged in user
if($user->id == Auth::user()->id){
// If yes add the $project to the $associated array
array_push($associated, $project);
}
endforeach;
endforeach;
// show the view
return View::make('clients.show')
->with('client', $client);
}
clients/show.blade.php
<?php $clients = $client->projects; ?>
#if (Auth::check())
#if (count($clients) > 0)
#foreach ($clients as $project)
<div class="one-third column">
<div class="projects">
<ul class="data">
<li><label>Project Name: </label><a class="btn btn-small btn-success" href="{{ URL::to('project/' . $project->id.'/show' ) }}"> {{ $project->project_name }}</a></li>
<li><label class="titletoggle">Project Brief <p>(click to toggle)</p></label><p class="brief">{{ $project->project_brief }}</p></li>
</ul>
<ul class='buttonslist'>
<li><button>Edit Project</button></li>
<li><button>Create Task</button></li>
<li><button>View Tasks</button></li>
</ul>
</div>
</div>
#endforeach
#else
<h3>You have no projects click here to create a project</h3>
#endif
#endif

The problem has to do with the way you are eager loading. Specifically this part.
$client->load(array('projects' => function($query)
{
// With the clients for each project
$query->with('user');
}));
The proper way to eager load these nested relationships would be.
$client->load(array(
'projects',
'projects.user',
));
Or more simply.
$client->load('projects.user');
Or you can set up the eager loading during the initial query.
$client = Client::with('projects.user')->find($id);
You also didn't mention that projects belongs to user. This relationship will need to be defined in the Project model.
class Project extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}
The lack of this method is probably the cause of the error message. Eloquent will forward calls to undefined methods to it's internal query builder object. The query builder object doesn't have a user() method, so that's why you get that error.

Related

How to Create a Hierarchy in Laravel with Optional Cutoffs

I am attempting to create a user hierarchy in Laravel 8, but have been bumping up against some issues.
Our use case is a very strange one based on the sales world. One user/sales rep may come in and begin working with us, then bring in their friend as a sub-user/sub-rep/“child” for them. The original “parent” user would want to see how their “child” is doing as far as sales go and such. Then that child user may bring in a few “children” users of their own over time. Sometimes, multiple parent users may come in as a group and do this, so the relationship seems like it should be many-to-many.
There are various one-off scenarios however where a child user may start their own sales company and while their personal sales may be viewed by the original parent user, anyone that they have recruited, they might only want to be viewed by the original child user and not the original parent user. This would be some sort of toggle able thing per user. It feels like a very involved use-case, so I’m not sure how to approach it fully.
Taking baby steps, my original idea was to create a many-to-many relationship with the users model having a user_user relationship table. Here is how it currently sits:
users table columns
id
Name
user_user table columns
parent_id
child_id
practice table columns
id
user_id (foreign id column to users table id)
name
user model
public function children() {
return $this->belongsToMany(User::class, 'user_user', 'parent_id', 'child_id');
}
public function parents() {
return $this->belongsToMany(User::class, 'user_user', 'child_id', 'parent_id');
}
public function practices() {
return $this->hasMany(Practice::class);
}
practice model
public function user() {
return $this->belongsTo(User::class);
}
Here is an example of a controller view attempting to find and combine sales locations/practices that a user at the top of a hierarchy would want to view for their entire structure.
// determine if viewing user has subreps
if (Auth::user()->children->count() == 0) {
$practices = User::find(Auth::user()->id)->practices()->orderBy('name', 'asc')->get();
} else {
// get the user's own practices
$own_practices = User::find(Auth::user()->id)->practices()->orderBy('name', 'asc')->get();
// create practices collection, then
// merge the user's own practices into collection
$practices = new Collection;
$practices = $practices->merge($own_practices);
foreach (Auth::user()->children as $child) {
// get each child's practices, then
// merge each practice into the practices collection
$child_practices = User::find($child->id)->practices()->orderBy('name', 'asc')->get();
$practices = $practices->merge($child_practices);
}
// sort the final practices collection alphabetically
$practices = $practices->sortBy('name');
}
And here is an example of a hierarchy.blade.php unordered list I was trying to work on to see just how much work would be needed to dig into it all.
<ul>
#foreach ($users as $user)
#if ($user->children->count() == 0)
<li>
<a href="{{ route('users.show', $user->id) }}">
{{ $user->name }}
</a>
</li>
#else
<li>
<a href="{{ route('users.show', $user->id) }}">
{{ $user->name }}
</a>
<ul>
#foreach ($user->children->sortBy('name') as $child)
<li>
<a href="{{ route('users.show', $child->id) }}">
{{ $child->name }}
</a>
</li>
#endforeach
</ul>
</li>
#endif
#endforeach
</ul>
As you can see, this works currently for a 1-deep level with some foreach looping, but I just can’t see a way to infinitely scale this in an efficient manner, much less have situations of being able to toggle cutoffs at different levels of viewership depending on user preference. I'd appreciate being pointed in a better direction for this hierarchal issue.

Beginner in Laravel - Eager loading seems to not work?

I'm new to Laravel and I'm trying to make a post where the user's post can receive likes from other users. But I'm trouble at when you click the user, it was suppose to direct to the users page where you can see all its post and the likes the it received. This is the code that suppose to do that work:
This is the controller:
public function index(User $user)
{
$posts = $user->posts()->with('user', 'likes')->get();
return view('users.posts.index', [
'user' => $user,
'posts' => $posts,
]);
}
}
and this is the view/template:
<div class="flex justify-center">
<div class="w-8/12 bg-white p-6 rounded-lg">
{{$user->name}}
</div>
</div>
I don't know what's the problem though, or is it a bug with Laravel eager loading?
But unfortunately it only returns a blank page, there's no error though.
The thing is after putting this code inside dump and die, there's data showing from the database. I don't why it doesn't show. Can someone help me with this problem? Much appreciated
Following this tutorial: https://www.youtube.com/watch?v=MFh0Fd7BsjE
Tutorial Github: https://github.com/codecourse/posty-traversy-media
If you want to access the posts you can iterate through them:
#foreach($posts as $post)
{{ $post->title }}
{{ $post->likes->count() }}
#endforeach
Not sure what data or relationships you want to access.
Check your route whether it has a user id for model injection
eg:/index/{user}
also in your blade example, there is no posts are used.you are using the user object
public function index(User $user)
{
$posts = $user->posts()->with('user', 'likes')->get();
return view('users.posts.index')->with([
'user' => $user,
'posts' => $posts,
]);
}

Trouble fetching data from 1 to 1 relationsip Laravel

I have set up two model with its row in table. And made a single form to fill both tables and it works perfectly
Tour.php
public function featuredImage()
{
return $this->hasOne('App\FeaturedImage');
}
tours table
id|name|content|featured_status
featuredImage.php
public function tour()
{
return $this->belongsTo('App\Tour');
}
Featured_images table
id|tour_id|path|name
Code in my controller to pass data to view.
$tours = Tour::where('featured', 1)->get();
return view('public.pages.index')
->withTours($tours);
Code in my view
#foreach($tours as $featured)
<div class="thumbnail">
<img src="{{$featured->featuredimage->path}}" alt="{{$featured->featuredImage->name}}">
</div>
<h4>{{$featured-name}}</h4>
#endforeach
The trouble is I'm not able to fetch featured images by writing
{{$featured->featuredimage->path}}
and the error is
Trying to get property of non-object
on the line {{$featured->featuredimage->path}}. I have used this method in my previous project and it had worked perfectly but it isn't going well in this one.
I tried replacing {{$featured->featuredimage->path}} with {{$featured->featuredImage->path}} but didn't worrked out.
Do this:
{{ $featured->featuredImage()->path }}
Also, you're creating a lot of additional queries here. You should use eager loading to solve N + 1 problem:
$tours = Tour::with('featuredImage')->where('featured', 1)->get();
And display data with:
{{ $featured->featuredImage->path }}

How to show related items with pagination in Eloquent and Blade?

AdminsController
public function index(){
$someMessages=$this->blog->paginate(5);
$users=User::all();
// return 'Welcome Admin';
return View::make('admin.admin',['someMessages'=>$someMessages,'users'=>$users]);
}
admin.blade.php
Here need to display username from 'users' variable based on uid stored in 'someMessages' object.
#extends('layout.default')
#section('title')
<title>Welcome Admin</title>
#stop
#section('content')
#foreach($someMessages as $message)
<blockquote>{{$message['blog']}}
<small>
<cite>
</cite>
</small>
</blockquote>
#endforeach
{{ $someMessages->links()}}
#stop
If you have a Eloquent relation between blog messages and users defined correctly you don't need to query the users separately. What you want to do is just go with the eager loading:
//in case you call it author in your "relation definition"
$someMessages = $this->blog->with('author')->paginate(5);
And then in your Blade template:
#foreach($someMessages as $message)
<div>
<div>{{ $message->blog }}</div>
<div>{{ $message->author->name }}</div>
</div>
#endforeach
If you haven't declared the relationship yet - it's easy. Open up your user eloquent model class and add a method:
public function blogs()
{
return $this->hasMany('Blog', 'uid');
}
In the blog eloquent model class you'd add:
public function author()
{
return $this->belongsTo('User', 'uid');
}
More about eloquent and relationships can be found here.
If that's not the case you can still filter your users collection to get the one you want:
#foreach($someMessages as $message)
<?php
$user = $users->filter(function($user) use ($message)
{
return $user->id == $message->uid;
})->first();
?>
<div>
<div>{{ $message->blog }}</div>
<div>{{ $user->name }}</div>
</div>
#foreach
What happens here is you filter your users collection based on $message->uid value and take the first one from it (it should always be just one or none since user IDs are unique).
Although you need to understand that in this case you will get all the users from database and filter through them for every blog message you're outputting. Eager loading is a much better idea here and I'd stick to it if possible.

Displaying a checkbox only for the shows(in the tvshow table) that don't match the entries for a given user id in the watchedtvshow table - Laravel

This is what I want to do:
Display a checkbox for the tvshow field entry(from the tvshow table) only if that field entry doesn't match the entries for a given user id in the 'watchedtvshow' table
Table structure for 'tvshow':
id tvshow
Table structure for 'watchedtvshow'
id uid tvid(id of the tvshow)
Here is my controller method:
$tvshow = TVShow::with('watchedtvshow')->get();
return View::make('browse',['tvshow' => $tvshow]);
My View:
#foreach($tvshow as $show)
{{ $show->title }} {{ 'a checkbox' }}
#endforeach
What I tried:
In my controller method:
$tvshow = TVShow::with('watchedtvshow')->get();
$uid = NULL;
if(Auth::check())
$uid = Auth::user()->id;
return View::make('browse',[
'tvshow' => $tvshow,
'uid' => $uid,
]);
In my view:
#foreach($tvshow as $show)
{{ $show->title }}
#foreach($show->watchedtvshow as $watchedtvshow)
#if($watchedtvshow->uid == $uid)
{{'don't show checkbox'}}
#else
{{'show checkbox'}}
#endif
#endforeach
#endforeach
The problem:
The thing is the second foreach loop executes only for the times it finds a watched tv show, otherwise it doesn't. So it just won't show any checkboxes.
I'm not an experienced coder, haven't really encountered anything like this before, I've spent three whole days trying to solve this, using count, for loops and what not, but I can't. Does anybody know how to achieve this?
First off a quick pointer: you can get the logged-in user's ID with Auth::id() rather than having to set it to null, then check if they're logged in then get the id directly off the model.
As for your problem, you're quite right that using the code you have you won't be getting the full story. What you need to do is get a list of all TV shows (regardless of user having it) and additionally a list of all TV shows the user has seen. Now, you can do this many ways, but the best 'Laravel way' is to model the relationship between User and TVShow. Your code doesn't mention this so I won't assume you have already done it. Your database is, of course, already set up for this so all you need to do is create the relationship. In this case, the relationship you need is a belongsToMany (a user can 'have' (have watched) many shows, and a show can 'have' (have been watched by) many users:
// in User.php
public function shows()
{
return $this->belongsToMany('TVShow', 'watchedtvshow', 'uid', 'tvid');
}
// in TVShow.php
public function users()
{
return $this->belongsToMany('User', 'watchedtvshow', 'tvid', 'uid');
}
Once you have this you can get a list of all users that have watched a show with:
$show->users;
Or you can get a list of all shows a user has watched with:
$user->shows;
Now putting that all together you should use Laravel's collections to detect whether a given item if in both arrays:
// in the controller:
$shows = TVShow::all();
if (Auth::check()) {
$watched = Auth::user()->shows;
} else {
// just create an empty collection so we can assume a consistent API
$watched = new \Illuminate\Support\Collection;
}
return View::make('browse', compact('shows', 'watched'));
// in browse.blade.php
#foreach($shows as $show)
{{ $show->title }}
#if ($watched->contains($show)
<span class="glyphicon glyphicon-ok"></span>
#else
<span class="glyphicon glyphicon-remove"></span>
#endif
#endforeach
Something like that?

Categories