Aggregate distant relationship with eloquent - php

I have two models User and Child.
class User extends Model {
public function children() {
return $this->belongsToMany('App\Child', 'user_child')->withPivot('relation_id');
}
public function connections() {
// How to get the distinctly aggregate of my children.friends.contacts as a relationship here?
}
}
class Child extends Model {
public function contacts() {
return $this->belongsToMany('App\User', 'user_child')->withPivot('relation_id');
}
public function friends() {
return $this->belongsToMany('App\Child', 'child_connection', 'child_id1', 'child_id2');
}
}
I would like to eagerly load the distant relationship which I name 'connections' which are the contacts (users) of my children's friends.
$user = User::with('children', 'children.friends', 'connections');
Any ideas how to do this elegantly?
Thank you!

Try
public function connections() {
return $this->belongsToMany('App\Child', 'user_id')->with('friends.contacts;);
}
OMG What a long shot!
If by any miracle that works, then doing
$user = User::find(1);
$user->connections(); //should bring 'children', 'friends' and 'contacts' in one query.
Well it should.
If you find an answer, please post it. I'm really interested in this.

Related

Cannot access Intermediate table in HasManyThrough relationship

My DB schema looks like this.
Now, in artisan tinker mode, When I try to query Details table from user Model, it shows me the records of the details table but I cannot access the the Cases Model for some reason, it always returns NULL in tinker.
This is my User Model
public function details()
{
return $this->hasManyThrough('App\Models\Detail', 'App\Models\Cases', 'user_id', 'case_id', 'id', 'id');
}
What am I doing wrong?
If for convenience you want to access Details directly from the User model then you can define relations as - (may seem like a little duplication but worth if it results in ease)
class User extends Model
{
public function cases()
{
return $this->hasMany(Case::class);
}
public function details()
{
return $this->hasManyThrough(Detail::class, Case::class);
}
}
class Case extends Model
{
public function details()
{
return $this->hasMany(Detail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class Detail extends Model
{
public function case()
{
return $this->belongsTo(Case::class);
}
}
Now both cases and details can be directly accessed via User record
$user->cases;
$user->details;
The idea of hasManyThrough is to skip the intermediate table. If you need to look at the cases and the details maybe you should define other relations for it.
// User model
public function cases()
{
return $this->hasMany(Cases::class, 'user_id');
}
// Cases model
public function details()
{
return $this->hasMany(Detail::class, 'user_id');
}
$users = User::with('cases.details')->get();
foreach ($users as $user) {
// an user
foreach ($user->cases as case) {
// a case
foreach ($case->details as $detail) {
// the details of a case
}
}
}

Laravel Eloquent elation for pivot table

I'm currently working on a laravel project, but I'm kind of stuck finding the right eloquent relations.
My tables and the connections (should) look like this:
Project Relations
My model relations look like this:
User
public function team()
{
return $this->hasMany(Team::class, 'user_id');
}
public function evaluation()
{
return $this->hasMany(Evaluation::class, 'user_id');
}
Team
public function user()
{
return $this->belongsTo(User::class);
}
public function survey()
{
return $this->hasMany(Survey::class, 'team_id');
}
Evaluation
public function user()
{
return $this->belongsTo(User::class);
}
public function survey()
{
return $this->hasMany(Survey::class, 'evaluation_id');
}
Survey
public function team()
{
return $this->belongsTo(Team::class);
}
public function evaluation()
{
return $this->belongsTo(Evaluation::class);
}
public function surveyresponse()
{
return $this->hasMany(SurveyResponse::class, 'survey_id');
}
SurveyResponse
public function survey()
{
return $this->belongsTo(Survey::class);
}
public function testquestion()
{
return $this->belongsTo('App\TestQuestion');
}
Is this the way to go? Do I need a "Has Many Through" relation here? Or a "Polymorphic Relationship"?
Seems correct to me, i just didnt see the TesteQuestion model (your last relation).
Answering your question:
The HasManyThrough relation is just a shortcut for accessing distant relations via an intermediate relation, in your case: Users has many evaluations that has many surveys. With this relationship you could get all surveys from a user.
Your relation would look like this:
/**
* Get all of the surveys for the user.
*/
public function surveys()
{
return $this->hasManyThrough('App\Survey', 'App\Evaluation');
}
You can access this relation like this:
$user->surveys();
But you can achieve the same (without using the HasManyThrough) by doing:
$user->evaluations()->surveys();
Beware that this will return the evaluations too, not just the surveys and it requires more processing.
So i recommend you doing the HasManyThrough relationship if you pretend to access the surveys a lot.

Laravel defining relationship

Am still new to laravel
I have the following tables
user
id
first_name
last_name
educations
id,
user_id //references user id
type
Now in my user model i would like to get a specific users educations which can be many
so a user can have many educations but each education belongs to a single user.
So in my user model i have
public function education()
{
return $this->hasMany('App\ApplicantEducation','id','user_id');
}
In my Education model i have
public function user()
{
return $this->belongsTo('App\User','user_id','id');
}
But the above fails, I cannot retrieve user specific educations
Where am i going wrong?
try this:
in User Model:
public function educations()
{
return $this->hasMany('App\ApplicantEducation', 'user_id');
}
in Education Model:
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
Change return $this->hasMany('App\ApplicantEducation','id','user_id');
to return $this->hasMany('App\ApplicantEducation','user_id', 'id'); you also ommit the id and user_id.
As your foreign_key is well formed, you can also rwite this simple code,
class User{
public function education()
{
return $this->hasMany('App\ApplicantEducation');
}
}
Class Educations{
public function user()
{
return $this->belongsTo('App\User');
}
}
Here,
$this->hasMany('App\ApplicantEducation','id','user_id');
In above statement first argument should be Model second should be foreign key and the third one is any other key from Education model.
Here, second and third arguments are not mandatory.
In User Model
class User...{
public function education()
{
return $this->hasMany('App\ApplicantEducation');
}
In Education Model
public function user()
{
return $this->belongsTo('App\User');
}
Here, additional parameters are not mandatory might be your addition parameters creates issue,
and now you can retrieve your user with Education by
$user = User::with('education')->get();
This can retrieve all the users with their education.
I hope it helps,Thank you, Happy coding.
You should try this:
Education Model
public function user()
{
return $this->belongsTo('App\User','user_id);
}
User Model
public function education()
{
return $this->hasMany('App\ApplicantEducation');
}

Using a polymorphic relation in a child model causes an infinite loop?

This question was already asked here but it received no answer. Now I face the same problem but in laravel 5.4. I have a model Book, a model ReadingSession and a model Comment. A book has many reading sessions and has many comments but the reading session can also have comments. So I have my relations defined like this:
Book.php
protected $with = [
'author',
'readingSessions',
'userRating',
'ratings',
'comments'
];
public function users()
{
return $this->belongsToMany(User::class, 'user_book');
}
public function author()
{
return $this->belongsTo(Author::class);
}
public function allReadingSessions()
{
return $this->hasMany(ReadingSession::class);
}
public function readingSessions()
{
return $this->hasMany(ReadingSession::class)
->where('user_id', Auth::user()->id);
}
public function ratings()
{
return $this->hasMany(Rating::class);
}
public function userRating()
{
return $this->hasMany(Rating::class)
->where('user_id', Auth::user()->id);
}
public function comments()
{
return $this->morphMany('App\Models\Comment', 'commentable');
}
ReadingSession.php
protected $with = ['comments'];
public function user()
{
return $this->belongsTo(User::class);
}
public function book()
{
return $this->belongsTo(Book::class);
}
public function comments()
{
return $this->morphMany('App\Models\Comment', 'commentable');
}
Comment.php
public function commentable()
{
return $this->morphTo();
}
These seems to create an infinite loop. Can anyone hint me on what I'm doing wrong?
The main reason you might have an infinite loop there is if you are trying to load automatically a relationship that in turn tries to do the same with the previous model.
Putting it into an example:
Book.php
protected $with = [
'author',
];
public function author()
{
return $this->belongsTo(Author::class);
}
Author.php
protected $with = [
'books',
];
public function books()
{
return $this->hasMany(Book::class);
}
In this case, every time you fetch an author it will fetch automatically his books that in turn will try to fetch the author and on and on...
One other thing that might happen and it's harder to realize is when using the $appends property on some accessors. If you are trying automatically had a variable into a model through the $appends and if that accessor fetches a relation or uses a relation in some way you might get an infinite loop again.
Example:
Author.php
protected $appends = [
'AllBooks',
];
public function books()
{
return $this->hasMany(Book::class);
}
public function getAllBooksAttribute() {
return $this->books->something...
}
In this case, every time the app tries to resolve your Author model it will fetch the books, that in turn will fetch the Author, that in turn will fetch the books again and on and on...
From your snippets, is not clear what is causing the problem but this answer might give some leads where to search for it.
To solve it, you might remove the relation from the $with and load it manually: $author->load('books') or Author::with('books')->where...
You can also load a relation of a relation in this way, for example: $author->load('books', 'books.comments') or Author::with('books', 'books.comments')->where...
It all comes down what you are trying to achieve. So you have to evaluate what and what not you should auto-load.
Be careful when loading automatically relations on your models and when adding accessors to $appends, especially if they use relations. It is an awesome feature but can bite hard sometimes.

Distant HasManyThrough

I have four Models:
User
Client
Store
Opportunity
The relationships are defined as such:
User hasMany Client
Client hasMany Store
Store hasMany Opportunity
User hasManyThrough Store, Client (this works)
The problem is that I'm attempting to access the User->Opportunity relationship via built-in Laravel relationships, but it doesn't seem as if I can do it without a custom Query or an additional user_id column on the opportunities table to allow direct access (even though one can be inferred from the Store->Client relationship). I'm also not a fan of nested foreach loops if they can be avoided.
My question:
Is there a way to go one level deeper and directly access a User's Opportunities in this scenario? The actual Model code and all relevant relationships are as follows:
User
class User extends Eloquent{
public function clients(){
return $this->hasMany('Client');
}
public function stores(){
return $this->hasManyThrough('Store', 'Client');
}
public function proposals(){
return $this->hasMany('Proposal');
}
public function opportunities(){ //This does the job, but I feel like it could be better
return Opportunity::join('stores', 'stores.id', '=', 'opportunities.store_id')->
join('clients', 'clients.id', '=', 'stores.client_id')->
join('users', 'users.id', '=', 'clients.user_id')->
select('opportunities.*')->
where('users.id', $this->id);
}
public function getOpportunitiesAttribute(){ //This just helps mimic the hasManyThrough shorthand
return $this->opportunities()->get();
}
}
Client
class Client extends Eloquent{
public function stores(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunities(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Store
class Store extends Eloquent {
public function client(){
return $this->belongsTo('Client');
}
public function opportunities(){
return $this->hasMany('Opportunity');
}
}
Opportunity
class Opportunity extends Eloquent {
public function store(){
return $this->belongsTo('Store');
}
}
I don't think there is such method in Laravel. You have to create your custom query. This custom query can be very expensive since multiple queries will be performed. Thus, the optimum solution for this, according to me, is to relate User and Opportunity with a foreign key.
However, if you don't desire to link User and Opportunity with a foreign key, then you can create a custom query to handle this. Simply add a "hasManyThrough" relation between Opportunity and Client model like,
<?php
class Client extends Eloquent{
public function store(){
return $this->hasMany('Store');
}
public function user(){
return $this->belongsTo('User');
}
public function opportunity(){
return $this->hasManyThrough('Opportunity', 'Store');
}
}
Then create a static function in User model.
<?php
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
public function client(){
return $this->hasMany('Client');
}
public function store(){
return $this->hasManyThrough('Store', 'Client');
}
public static function getOpportunityOfUser($userId)
{
$clients = User::find($userId)->client;
foreach ($clients as $client) {
$opportunities[] = Client::find($client->id)->opportunity;
}
return $opportunities;
}
}
Now you can access Opportunity realted to a User in one go like,
Route::get('/', function()
{
return $usersOpportunities = User::getOpportunityOfUser(1);
});
This will return all opportunity of all clients related to User with id '1'.
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function opportunities() {
return $this->hasManyDeep(Opportunity::class, [Client::class, Store::class]);
}
}

Categories