I have 3 tables:
Contract, with id
Contract_User, with contract_id and user_id
User, with id
I'm trying to use hasManyThrought to get the User information when I have a contract, but I'm having trouble.
Is it possible?
Thanks
I agree with Tim Lewis. I think this is just a "simple" BelongsToMany. Depending on what your models look like, here is an example of what they could/should look like.
If you are using something other than the default foreign keys, you'll need to provide those column(s) as extra parameters. You can find more on that in the link above as well. Hope this helps!
User.php
/**
* The contracts that belong to the user.
*/
public function contracts()
{
return $this->belongsToMany('App\Contract');
}
Contract.php
/**
* The users that belong to the contract.
*/
public function users()
{
return $this->belongsToMany('App\User');
}
Then somewhere in your controller, you'd get to the user's contracts by:
$user = App\User::find(1);
foreach ($user->contracts as $contract) {
// $contract->name;
}
Related
I'm after a bit of logic advice. I am creating a system where users login and register their participation at an activity. They can participate at an activity many times. What is the best way to do this? I want to ensure I can use eloquent with this rather than creating my own functions.
I am imagining...
Users:
id
Activitys:
id
name
Participations:
id
user_id
activity_id
time_at_activity
I want to later be able to do such things as:
$user->participations->where('activity_id', 3)
for example.
What is the best way to set this up? I had in mind..
User: hasMany->Participations
Activity: belongsTo->Participation
Participation: hasMany->Activitys & belongsTo->User
Does this look correct?
The users schema can relate to activities through a pivot table called participations:
/**
* Indicate that the model belongs to a user.
*
* #see \App\Model\User
*
* #return BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Indicate that the model belongs to many activity participations.
*
* #see \App\Model\Activity
*
* #return BelongsTo
*/
public function participations()
{
return $this->belongsToMany(Activity::class, 'participations');
}
$user->participations()->attach($activity);
You may want to add the reciprocal relationships. These can be separated out into traits for code reuse. ->attach(['participated_at' => now()])
You can use Many-to-Many Relationship.
Use Participation as your pivot table. Define relationships as
/* in User */
public function activity()
{
return $this->belongsToMany('App\Models\Activitys','participation','user_id','activity_id')->as('participation')->withPivot('time_at_activity');
}
/* in Activity */
public function user()
{
return $this->belongsToMany('App\Models\Users','participation','activity_id','user_id')->as('participation')->withPivot('time_at_activity');
}
DB schema
// App\User
public function participations()
{
return $this->hasMany('App\Participation');
}
// You may create App\Participation Model
// App\Participation
public function user()
{
return $this->belongsTo('App\User');
}
// Controller
$userParticipations = $user->participations->where('activity_id', 3);
// eager loading version
$userWithParticipations = $user->with(['participations' => function($q) { $q->where('activity_id', 3) }])->get();
I want to use sophisticated SELECT query with JOINs and GROUP BY in Laravel model.
Сoncretely I want to make a messager in my application. Here is table "messages" with all messages. Now I want to create model called "Dialog". Keep in mind here is no table "dialogs", a dialog is a result of joining and grouping.
Example of query:
SELECT
cl.name AS client_name,
COUNT(m.id) AS messages_count,
MAX(m.created_at) AS last_message,
COUNT(m.id) > SUM(m.viewed_by_client) AS has_new_for_client,
COUNT(m.id) > SUM(m.viewed_by_user) AS has_new_for_user
FROM messages AS m
INNER JOIN clients AS c ON m.client_id = c.id
GROUP BY c.id
Of cource I can use raw SQL queries. But I want to use Eloquent relations later with all its benefits. For example:
$dialog->client->full_name
$dialog->client->order->ordered_items
I had an idea to create a VIEW in database from my query and to use this view as a fake table in the model. But it seems to me not ideal solution.
So, how can I use JOINs and GROUP BY in Eloquent when I do not have a real table for model entities? Or may be some different solutions for my task?
You can have a database table without an Eloquent model but not the other way around. That said, there's no rule against making more than 1 model per table. Not really standard practice though.
I experimented with making a model that would inherit from another model but the boot method didn't work as expected so I dropped it.
I think you could get all the information you take from that query with accessors in your Client model. Since your query has no where clause, a scope is not really necessary but it could also be done with that.
OPTION 1: Accessors
# App\Client
class Client extends Model
{
// Standard Eloquent relationship
public function messages()
{
return $this->hasMany(App\Message::class);
}
// Accessor $client->client_name
public function getClientNameAttribute()
{
return $this->name;
}
// Accessor $client->last_message
public function getLastMessageAttribute()
{
// Load relationship only if it hasn't been loaded yet
if(!$this->relationshipLoaded('messages'))
$this->load('messages');
// use max() method from collection to get the results
return $this->messages->max('created_at');
}
// Accessor $client->has_new_for_client
public function getHasNewForClientAttribute()
{
// Load relationship only if it hasn't been loaded yet
if(!$this->relationshipLoaded('messages'))
$this->load('messages');
return $this->messages->count() > $this->messages->sum('viewed_by_client');
}
// Accessor $client->has_new_for_user
public function getHasNewForUserAttribute()
{
// Load relationship only if it hasn't been loaded yet
if(!$this->relationshipLoaded('messages'))
$this->load('messages');
return $this->messages->count() > $this->messages->sum('viewed_by_user');
}
}
And then you can access all the properties dynamically
$dialog = Client::withCount('messages')->find($id);
$dialog->client_name;
$dialog->messages_count;
$dialog->has_new_for_client;
$dialog->has_new_for_user;
$dialog->last_message;
However if you're converting $dialog to an array or json format, accessors will be lost unless you append them. In the same way, you can hide the attributes you don't want to show.
This can be done globally for the model
protected $appends = ['client_name', 'has_new_for_client', 'has_new_for_user', 'last_message'];
protected $hidden = ['name'];
or locally for the query
$dialog->setHidden(['name']);
$dialog->setAppends(['client_name', 'has_new_for_client', 'has_new_for_user', 'last_message'];
OPTION 2: Query scopes
# App\Client
class Client extends Model
{
public function scopeDialog($query)
{
$query->select('name as client_name')
->withCount('messages') // the default name will be messages_count
->selectRaw('max(m.created_at) as last_message')
->selectRaw('count(m.id) > sum(m.viewed_by_client) as has_new_for_client')
->selectRaw('count(m.id) > sum(m.viewed_by_user) as has_new_for_user')
->join('messages as m', 'm.client_id', 'clients.id')
->groupBy('clients.id');
}
}
And then just call it like you would any scope Client::dialog()->...
OPTION 3: Just use whatever methods are already available instead of writing more logic
$dialog = Client::with('messages')->find($id);
// client_name
$dialog->name
// messages_count
$dialog->messages->count()
// last_message
$dialog->messages->max('created_at')
// has_new_for_client
($dialog->messages->count('id') > $dialog->messages->count('viewed_by_client'))
// has_new_for_user
($dialog->messages->count('id') > $dialog->messages->count('viewed_by_user'))
Create dialogs table and put 'dialog_id' column into the messages table. Each message has a dialog and a client. Create relationships in each model. So you can access attributes over models as you want. By doing this, this code works;
$dialog->client->full_name
$dialog->client->order->ordered_items
I am trying to detail example about how to get User Model's Accessor in another model with using relationship
Suppose, we have User table & Comment Table...
Now, Suppose I appends User's Profile Full URL in User model using "getProfilePhotoUrlAttribute" Method. when I call User model eloquent then it's appends User Profile Image automatically.
but Now I wants to get that user's profile Full URL in with Comments then we can't access Accessor using Join because with join we can join only out DataBase's Table Columns. If we have profile_photo_path column & doesn't have profile_photo_url named column as we define accessor function name then we can't access using jjoin. in this case we wants to use Relationship method
For example:-
Case :- 1 You wants to Get the user's comments with User details
In this case, User have one or more than one comments So we need to use One TO Many Relation
App/Models/User.php file
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = [
'profile_photo_url',
];
/**
* Get the URL to the user's profile photo.
*
* #return string
*/
public function getProfilePhotoUrlAttribute()
{
... here return full profile URL (concat profile_path with storage/public location path)...
}
/**
* Get the user's comments with User details.
*
* One To Many relation
*/
public function comments()
{
return $this->hasMany(Comment::class);
}
Now then, use Model eloquent Query like below
$user = User::with('comments')->where('id', '=', '2')->get();
echo '<pre>';
print_r($user->toarray());
Case :- 2 You wants to Get the user details of the all comments.
In this case, we need to use Many TO One Relation
App/Models/Comment.php file
/**
* Get the user details of the comments.
*
* One To Many (Inverse) / Belongs To
*/
public function user()
{
return $this->belongsTo(User::class);
}
then use Model eloquent Query like below
$comments = Comment::where('deal_id', '=', '45')->get();
print_r($comments->toarray());
foreach ($comments as $comment) {
print_r($comment->user->toarray());
echo $comment->user->profile_photo_url;
echo "<br/>";
}
NOTE:- I used Latest version - it is Laravel 8, So Syntax may vary as per your Laravel Version
For More Detail with Output Data check here my answer on another question
& you can check it in Laravel Official Documentation
I am trying to make a ticketing system and I am working on showing the tickets a user has. The way the relationship works now is that the tickets are assigned to a user who's then assigned to a campus. I actually need it to to be where the tickets are assigned to the campus since the campus is definite, and have a tech assigned to that campus. The tech should only see the tickets that belongs to the campus that the tech is assigned to.
In theory, it should work like if a user is changed or relocated then all that needs to be done is update the TechID column. Here is how the table relationship works currently:
Tickets (PK - TicketID, FK - CampusID)
Campus (PK - CampusID, FK - TechID)
User (PK - TechID)
I actually have a similar question that I asked before, but this was before I realized that the way it was structured before isn't the ideal setup.
Here is the link to the previous question: Previous Question
Here is my current controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use App\User;
use App\Campus;
use App\Ticket;
class PagesController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
// Dashboard Page
public function index()
{
$user = User::with(['campuses' => function($query) {
$query->select(['TechID', 'CampusName']);
},'tickets'])->where('id', Auth::id())->first();
$campuses = Campus::all()->where('TechID', $user->id);
$tickets = Ticket::all()->where('CampusID', $campuses->pluck('CampusID'));
return view('home')->with([
'user' => $user,
'campuses'=>$campuses,
'tickets'=>$tickets]);
}
// Queue Page
public function Queue() {
return view('Pages.Queue');
}
// Reports Page
public function Reports() {
return view('Pages.Reports');
}
// Search Page
public function Search() {
return view('Pages.Search');
}
}
I was hoping this would work, but no tickets actually show up. The user can have more than one campus so I was wondering if maybe this is where things are messing up. Any help is much appreciated
I'd suggest doing the where before the all/get, so that the filtering is done on the database level instead of PHP -- all before where will have the database return all of the values, then use the Collection to filter the results. Also, the second query needs whereIn, since you're passing in an array of values:
$campuses = Campus::where('TechID', $user->id)->get();
$tickets = Ticket::whereIn('CampusID', $campuses->pluck('CampusID'))->get();
I need to implement a follow system like twitter but with exception a user can follow many Post and can follow whole Category or a user can follow a User.
I have come up with this relationship. I am using Laravel 5.1
User Model
public function followers()
{
return $this->belongsToMany('App\User', 'user_follows', 'user_id', 'follow_id');
}
public function follows()
{
return $this->belongsToMany('App\User', 'user_follows', 'follow_id', 'user_id');
}
and for follow a Category
Category Model
public function followers()
{
return $this->belongsToMany('App\User', 'category_follows', 'user_id', 'category_id');
}
and for Post is the same way, as you can see I need 3 tables (user_follows, category_follows, post_follows) to make this work.
I know there is Polymorphic Relation but I cant wrap my head around it.
Please help how i can simplify it. once again below are the requirements
User can follow many Posts
User can follow many Category
User can follow many User
You can use morphedByMany to create polymorphic many to many relations. Instead of having separate *_follows tables, you can have a single followables table with the following schema:
user_id int # user_id that is following something
followable_id int # id of the thing that is being followed
followable_type string # type of the thing that is being followed
Here's a sample implementation:
Category, Post and User models
/*
* This function gets the followers of this entity. The
* followable_id in the followables relation would be
* the id of this entity.
*/
function followers() {
return $this->morphToMany('App\User', 'followable');
}
User model
/*
* Gets the list of users that are followed by this user.
*/
function users() {
return $this->morphedByMany('App\User', 'followable');
}
/*
* Gets the list of posts that are followed by this user.
*/
function posts() {
return $this->morphedByMany('App\User', 'followable');
}
/*
* Gets the list of categories that are followed by this user.
*/
function categories() {
return $this->morphedByMany('App\User', 'followable');
}
Note that in this case, a User is both morphed by many and morphed to many, creating a self-reference many to many relationship.
Every new followable entity you create, you will need to add the followers() function to that entity, and a corresponding inverse relation to the Users entity. You could define a Followable trait containing that function, and simply add use Followable; to the new entity you add.
I have a table called bonus. A user can get a bonus (it's like an reward) for certain actions. Well, the bonus can be assigned to many users and many users can get the same bonus. So it's a many to many relation between user and bonus.
This is no problem so far. But users can get the same bonus for different actions. So let's say there is a bonus for voting on a picture. Well, one user could vote on one picture and another one could vote on another picture which I'd like to save in the many-to-many table.
Furthermore there could be a bonus for writing a comment which is clearly another table than picture votes.
The problem here is that I would need to save the polymorphic type in the bonus table and the ID in the many-to-many table.
I think this should be the best way but how would I realize it with laravel? I think this is not a normal use case. But still I'd like to use it as other relations in laravel so that I could fetch a user and get his bonuses with the correct polymorphic relation.
Do you have any ideas?
You are probably going to have to develop your own relationship classes.
Ex:
MODEL
public function answers()
{
$instance = new Response();
$instance->setSid($this->sid);
return new QuestionAnswerRelation($instance->newQuery(),$this);
}
RELATIONSHIP
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\Relation;
use Pivotal\Survey\Models\Answer;
use Pivotal\Survey\Models\Collections\AnswerCollection;
use Pivotal\Survey\Models\QuestionInterface;
use Pivotal\Survey\Models\SurveyInterface;
class QuestionAnswerRelation extends Relation
{
/**
* Create a new relation instance.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param \Illuminate\Database\Eloquent\Model $parent
* #return void
*/
public function __construct(Builder $query, QuestionInterface $parent)
{
$table = $query->getModel()->getTable();
$this->query = $query
->select(array(
\DB::raw($parent->sid.'X'.$parent->gid.'X'.$parent->qid . ' AS value'),
'id'
));
$this->query = $query;
$this->parent = $parent;
$this->related = $query->getModel();
$this->addConstraints();
}
public function addEagerConstraints(array $models)
{
parent::addEagerConstraints($models);
}
public function initRelation(array $models, $relation)
{
}
public function addConstraints()
{
}
public function match(array $models, Collection $results, $relation)
{
}
public function getResults()
{
$results = $this->query->get();
$answerCollection = new AnswerCollection();
foreach($results as $result)
{
$answer = new Answer($result->toArray());
$answer->question = $this->parent;
$answerCollection->add($answer);
}
return $answerCollection;
}
In this case we are using Lime Survey which creates a unique table (note the $instance->setSid() changes the table name) for each of its surveys and a unique column for each of its answer -> question values. ( note $parent->sid.'X'.$parent->gid.'X'.$parent->qid. 'AS value')
Where sid = survey_id, gid = group_id(I think) and qid = question_id
Its was quite irritating.
Note how I reference values from the parent to further develop the query.
You should be able to follow a similar route to achieve whatever your heart desires and still maintain the feasibility to use Eloquent.