How should make this relationship and access data in Laravel - php

I have 4 tables:
Table name: clients
Fields: id, name, slug
Table name: projects
Fields: id, slug
Table name: project_translation
Fields: id, locale, project_id, title
Table name: client_project
Fields: id, client_id, project_id
Relationships
In the Project model
public function clients()
{
return $this->belongsToMany(Client::class,'client_project')->withTimestamps();
//return $this->belongsToMany('Client')->withTimestamps()->orderBy('priority', 'desc');
}
In the Client model
public function projects()
{
return $this->belongsToMany(Project::class,'client_project')->withTimestamps();
}
public function translate()
{
return $this->belongsToMany(ProjectTranslation::class,'project_translations')->withTimestamps();
}
In Client_Project model
public function clients()
{
return $this->hasMany('App\Models\Project');
}
public function projects()
{
return $this->hasOne('App\Models\Client');
}
In ProjectTranslation model
public function client()
{
return $this->hasMany('App\Models\Client');
}
And I'm trying to access data in controller like this:
$client_project = Client::find($id)->translate;
return $client_project;
This give me the next error:
SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique
table/alias: 'project_translations' (SQL: select
`project_translations`.*, `project_translations`.`client_id` as
`pivot_client_id`, `project_translations`.`project_translation_id` as
`pivot_project_translation_id`, `project_translations`.`created_at` as
`pivot_created_at`, `project_translations`.`updated_at` as
`pivot_updated_at` from `project_translations` inner join
`project_translations` on `project_translations`.`id` =
`project_translations`.`project_translation_id` where `project_translations`.`client_id` = 22)
I'm not sure, but I think something is wrong with Relationships.
I'm in Client blade, and I want to show the projectstranslations of the projects of this client.

Here I formatted you query so it's readable, as I said in the comments I am not a laravel user. But I know Sql
select
`project_translations`.*,
`project_translations`.`client_id` as `pivot_client_id`,
`project_translations`.`project_translation_id` as `pivot_project_translation_id`,
`project_translations`.`created_at` as `pivot_created_at`,
`project_translations`.`updated_at` as `pivot_updated_at`
from
`project_translations` <-- Duplicate Table with no alias
inner join
`project_translations` on `project_translations`.`id` = `project_translations`.`project_translation_id`
where
`project_translations`.`client_id` = 22

you don't need Client_Project model.
In the Project model
public function clients()
{
return $this->belongsToMany(Client::class,'client_project')->withTimestamps();
}
public function translates()
{
return $this->hasMany(ProjectTranslation::class);
}
In the Client model :
public function projects()
{
return $this->belongsToMany(Project::class,'client_project')->withTimestamps();
}
In the ProjectTranslation model :
public function project()
{
return $this->belongsTo('App\Models\Project');
}
In the controller :
$client_projects = Client::find($id)->projects; //all client projects
return $client_projects;
In the view after geting this $client_projects and looping over it you can get the translations of a project by :
$client_project->translates // for single projects you will get its translates :)
For Project you have many to many with clients and it has one to many translates => in the documentation links there are many examples :)

Related

Laravel Eloquent wherePivot method returns column 'pivot' not found

I have a many-to-many database relationship set up in an existing Laravel installation between users and agencies.
On the frontend I'm using a drop down filter to send an agency_id to the server that is supposed to filter/return all the users by this agency_id variable.
In my models:
User.php
/**
* The agencies that belong to the user.
*/
public function agencies()
{
return $this->belongsToMany('App\Agency');
}
Agency.php
/**
* The users that belong to the agency.
*/
public function users()
{
return $this->belongsToMany('App\User');
}
When I query the database, I check if the agency parameter is present in the header
->when($request->agency, function ($q) use ($request, $schema) {
// Determine whether agency_id foreign key exists or if it's a pivot relationship
if(Schema::hasColumn($schema, 'agency_id'))
{
return $this
->repository
->whereAgency($q, \Hashids::decode($request->agency)[0]);
}
else
{
return $this
->repository
->whereAgencyPivot($q, \Hashids::decode($request->agency)[0]);
}
})
In the repository I do the following:
public function whereAgencyPivot($q, string $agency)
{
return $q->wherePivot('agency_id', $agency);
}
This returns the following error:
Column not found: 1054 Unknown column 'pivot' in 'where clause'
I have also tried:
public function whereAgencyPivot($q, string $agency)
{
return $q->agencies()->where('agency_user.agency_id', $agency);
}
which returns:
Call to undefined method Illuminate\Database\Eloquent\Builder::agencies()
Is there anything I'm missing?
So you want to get all the users against a pre selected agency which is linked with users in many-many relationship. Please correct me if I have miss understood.
To get many to many relationship
Agency::with('users')->where('id', 1)->get(); // id is injected dynamically.
To conditionally load relationship
if(some condition) {
$agency->load('users'); // It'll fetch all users against the given agency.
}
To get only users information
$data = Agency::with('users')->where('id', 1)->first();
$data->users; // gives you only users collection
The answer ended up being:
return $q->whereHas('agencies', function($query) use ($agency) {
$query->where('id', $agency);
});

Laravel relationships get user emails

Hey from my website I'm sending multiple notifications to users, I'm assigning users to a team and then I assign this team to the notifications table.
However when I do SiteNotification::find(1)->notifications() then I get the name of the team, however, I was looking to get the user model and all the details related to that. Is there an easy way to obtain this using Laravel Eloquent relationships?
My DB model and Eloquent model are below;
DB tables;
User
id | username | email
Teams
id | name |
Team Members
team_id | user_id
Site Notifications
site_notification_id | team_id
Model Here:
class SiteNotification extends Model {
public function notifications()
{
return $this->belongsToMany(Team::class, 'site_check_notifications', 'site_check_id', 'team_id');
}
}
Update:
I've tried updating the Team Model as follows;
class Team extends Model
{
public function users()
{
return $this->hasManyThrough(
User::class,
TeamMember::class,
'team_id',
'id'
);
}
}
However this throws an error as follows when running this;
$site = Site::find(1);
foreach( $site->notifications as $notification) {
dd($notification->users);
}
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'team_members.id' in 'on clause' (SQL: select `users`.*, `team_members`.`team_id` from `users` inner join `team_members` on `team_members`.`id` = `users`.`id` where `team_members`.`team_id` = 4)
Any ideas what I'm doing wrong??
I've found a solution which has meant that I do not need to amend my existing database structure and I've found the correct relationship to use.
public function users()
{
return $this->belongsToMany(
User::class,
'team_members',
'team_id',
'user_id'
);
}
Now I can do Site::find(1)->users->pluck('email')
You have to change the model structure... This is how I would have reached your goal... Take it as a "working solution", maybe not the best!
First of all, database. You should have these tables, there is no need to
users => users table
teams => teams table
team_user => pivot table n:n
team_site_notification => pivot table n:n
site_notifications => notifications table
user_site_notification => pivot table n:n
Then you create the related models relations
public class User {
// [...]
public function teams() {
return $this->belongsToMany(Team::class)
}
public function notifications() {
return $this->belongsToMany(SiteNotification::class)
}
}
public class Team {
// [...]
public function users() {
return $this->belongsToMany(User::class)
}
public function notifications() {
return $this->belongsToMany(SiteNotification::class)
}
}
public class SiteNotification {
// [...]
public function teams() {
return $this->belongsToMany(Team::class)
}
public function users() {
return $this->belongsToMany(User::class)
}
}
In your controller, when you create the SiteNotification model, you'll have to associate also the users. For example
public function store(Request $request) {
// Do your stuff
$team = Team::findOrFail($request->your_team_id);
$notification = Notification::create($data);
$notification->teams()->associate($request->your_team_id);
// Retrieve the users from the team... Maybe not everyone should receive a notification
$team->users()->whereIn('id', $user_ids)->get()->pluck('id')
$notification->users()->associate($ids);
}
When you want to get your users list you simple retrive the associated users in this way:
dd($notification->users);
// [ User:{id: 1, '...'}, User:{id: 2}, User:{id: 7} ]
Hope this is what you're looking for!

Laravel - Not unique table/alias on Polymorphic Relationship

I have three tables, they are :
Contacts
contact_name
contact_email
contact_phone
Locations
location_name
location_country
Association Contacts
association_contact_id
association_contacts_id
contacts_type
In my models, I've set the Polymorphs...
AssociationContact
public function contact()
{
return $this->morphTo();
}
Client
public function contact()
{
return $this->morphToMany(
// the related model
'App\Models\AssociationContact',
// the relationship name
'contact',
// the table name, which would otherwise be derived from the relationship name - twowordables
'association_contacts',
// the foreign key will be twowordable_id, derived from the relationship name, which you're adhering to
'association_contact_id',
// the 'other' key, which would otherwise be derived from the related model's snake case name - two_word_id
'association_contacts_id'
);
}
When running the following code :
$clients = Client::all();
foreach($clients as $client)
{
echo "<pre>";
print_r($client->contact);
echo "</pre>";
}
I get the following error :
Syntax error or access violation: 1066 Not unique table/alias: 'association_contacts' (SQL: select `association_contacts`.*, `association_contacts`.`association_contact_id` as `pivot_association_contact_id`, `association_contacts`.`association_contacts_id` as `pivot_association_contacts_id`, `association_contacts`.`contact_type` as `pivot_contact_type` from `association_contacts` inner join `association_contacts` on `association_contacts`.`id` = `association_contacts`.`association_contacts_id` where `association_contacts`.`association_contact_id` = 7 and `association_contacts`.`contact_type` = App\Models\Client)
It must be how I've set up the model relations, But not sure what I've done wrong.
Cheers
Put this in AssociationContact:
public function contact()
{
return $this->morphTo(null,'contacts_type','association_contacts_id');
}
Change the relationship in your Client table to this:
public function contact()
{
return $this->morphMany('App\Models\Contact','contact','contacts_type','association_contacts_id');
}
This should work.

Laravel HasManyThrough with 3-way pivot table

I have the following models
Recipe
public function ingredientRecipeUnits()
{
return $this->hasMany(IngredientRecipeUnit::class);
}
Ingredient
public function ingredientRecipeUnits()
{
return $this->hasMany(IngredientRecipeUnit::class);
}
Unit
public function ingredientRecipeUnits()
{
return $this->hasMany(IngredientRecipeUnit::class);
}
and a pivot table (in its own model) connecting all three:
IngredientRecipeUnit
public function ingredient()
{
return $this->belongsTo(Ingredient::class);
}
public function recipe()
{
return $this->belongsTo(Recipe::class);
}
public function unit()
{
return $this->belongsTo(Unit::class);
}
I would like to fetch all Recipes through the ingredient model.
For this I made the following relationship:
public function recipes() {
return $this->hasManyThrough(
Recipe::class,
IngredientRecipeUnit::class,
'ingredient_id',
'id'
);
}
This generates an incorrect query looking like this
select * from `recipes`
inner join `ingredient_recipe_units`
on `ingredient_recipe_units`.`id` = `recipes`.`id`
where `ingredient_recipe_units`.`ingredient_id` = ?
while in reality the query should look like this. (Notice subtle change of id -> recipe_id on line 3)
select * from 'recipes'
inner join `ingredient_recipe_units`
on `ingredient_recipe_units`.`recipe_id` = `recipes`.`id`
where `ingredient_recipe_units`.`ingredient_id` = ?
Besides sending a pull-request to the Eloquent repo to add an extra Parameter or using raw SQL; is there any way to solve this?
This ended up being a mistake in the way I thought up the relationship and it was solved by simply defining a belongsToMany from the individual related models directly to the IngredientRecipeUnit table.
ex: Ingredient Model
public function recipes() {
return $this->belongsToMany(Recipe::class, 'ingredient_recipe_units');
}
Depending on your model you might have the possibility to add multiple of the same ingredient or unit, in this case you should mark the query with the instinct method.
Like this:
public function recipes() {
return $this->belongsToMany(Recipe::class, 'ingredient_recipe_units')->distinct();
}
Which correclty generates the following desired query:
select distinct * from `recipes`
inner join `ingredient_recipe_units`
on `recipes`.`id` = `ingredient_recipe_units`.`recipe_id`
where `ingredient_recipe_units`.`ingredient_id` = ?

How to set Eloquent relationship belongsTo THROUGH another model in Laravel?

I have a model Listing that inherits through its belongsTo('Model') relationship should inherently belong to the Manufacturer that its corresponding Model belongs to.
Here's from my Listing model:
public function model()
{
return $this->belongsTo('Model', 'model_id');
}
public function manufacturer()
{
return $this->belongsTo('Manufacturer', 'models.manufacturer_id');
/*
$manufacturer_id = $this->model->manufacturer_id;
return Manufacturer::find($manufacturer_id)->name;*/
}
and my Manufacturer model:
public function listings()
{
return $this->hasManyThrough('Listing', 'Model', 'manufacturer_id', 'model_id');
}
public function models()
{
return $this->hasMany('Model', 'manufacturer_id');
}
I am able to echo $listing->model->name in a view, but not $listing->manufacturer->name. That throws an error. I tried the commented out 2 lines in the Listing model just to get the effect so then I could echo $listing->manufacturer() and that would work, but that doesn't properly establish their relationship. How do I do this? Thanks.
Revised Listing model (thanks to answerer):
public function model()
{
return $this->belongsTo('Model', 'model_id');
}
public function manufacturer()
{
return $this->belongsTo('Model', 'model_id')
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id');
}
I found a solution, but it's not extremely straight forward. I've posted it below, but I posted what I think is the better solution first.
You shouldn't be able to access manufacturer directly from the listing, since manufacturer applies to the Model only. Though you can eager-load the manufacturer relationships from the listing object, see below.
class Listing extends Eloquent
{
public function model()
{
return $this->belongsTo('Model', 'model_id');
}
}
class Model extends Eloquent
{
public function manufacturer()
{
return $this->belongsTo('manufacturer');
}
}
class Manufacturer extends Eloquent
{
}
$listings = Listing::with('model.manufacturer')->all();
foreach($listings as $listing) {
echo $listing->model->name . ' by ' . $listing->model->manufacturer->name;
}
It took a bit of finagling, to get your requested solution working. The solution looks like this:
public function manufacturer()
{
$instance = new Manufacturer();
$instance->setTable('models');
$query = $instance->newQuery();
return (new BelongsTo($query, $this, 'model_id', $instance->getKeyName(), 'manufacturer'))
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id')
->select(DB::raw('manufacturers.*'));
}
I started off by working with the query and building the response from that. The query I was looking to create was something along the lines of:
SELECT * FROM manufacturers ma
JOIN models m on m.manufacturer_id = ma.id
WHERE m.id in (?)
The query that would be normally created by doing return $this->belongsTo('Manufacturer');
select * from `manufacturers` where `manufacturers`.`id` in (?)
The ? would be replaced by the value of manufacturer_id columns from the listings table. This column doesn't exist, so a single 0 would be inserted and you'd never return a manufacturer.
In the query I wanted to recreate I was constraining by models.id. I could easily access that value in my relationship by defining the foreign key. So the relationship became
return $this->belongsTo('Manufacturer', 'model_id');
This produces the same query as it did before, but populates the ? with the model_ids. So this returns results, but generally incorrect results. Then I aimed to change the base table that I was selecting from. This value is derived from the model, so I changed the passed in model to Model.
return $this->belongsTo('Model', 'model_id');
We've now mimic the model relationship, so that's great I hadn't really got anywhere. But at least now, I could make the join to the manufacturers table. So again I updated the relationship:
return $this->belongsTo('Model', 'model_id')
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id');
This got us one step closer, generating the following query:
select * from `models`
inner join `manufacturers` on `manufacturers`.`id` = `models`.`manufacturer_id`
where `models`.`id` in (?)
From here, I wanted to limit the columns I was querying for to just the manufacturer columns, to do this I added the select specification. This brought the relationship to:
return $this->belongsTo('Model', 'model_id')
->join('manufacturers', 'manufacturers.id', '=', 'models.manufacturer_id')
->select(DB::raw('manufacturers.*'));
And got the query to
select manufacturers.* from `models`
inner join `manufacturers` on `manufacturers`.`id` = `models`.`manufacturer_id`
where `models`.`id` in (?)
Now we have a 100% valid query, but the objects being returned from the relationship are of type Model not Manufacturer. And that's where the last bit of trickery came in. I needed to return a Manufacturer, but wanted it to constrain by themodelstable in the where clause. I created a new instance of Manufacturer and set the table tomodels` and manually create the relationship.
It is important to note, that saving will not work.
$listing = Listing::find(1);
$listing->manufacturer()->associate(Manufacturer::create([]));
$listing->save();
This will create a new Manufacturer and then update listings.model_id to the new manufacturer's id.
I guess that this could help, it helped me:
class Car extends Model
{
public function mechanical()
{
return $this->belongsTo(Mechanical::class);
}
}
class CarPiece extends Model
{
public function car()
{
return $this->belongsTo(Car::class);
}
public function mechanical()
{
return $this->car->mechanical();
}
}
At least, it was this need that made me think of the existence of a belongsToThrough
You can do something like this (Student Group -> Users -> Poll results):
// poll result
public function studentGroup(): HasOneDeep
{
return $this->hasOneDeepFromRelations($this->user(), (new User())->studentGroup());
}

Categories