I have 3 tables
type
type_id
person
person_id
category
category_id
table_name
table_id
person_id
In category I have connections of different tables/models with Type model, So if I have want to get type_id connected with person with person_id = 23 the query should look like this:
SELECT * FROM category WHERE table_name='person' AND table_id = 23
In my Person model I defined relationship with Type this way:
public function groups()
{
return $this->belongsToMany('Type', 'category',
'table_id', 'type_id')->wherePivot( 'table_name', '=', 'person' );
}
When I want to get those types and I use:
$person->groups()->get()
The query looks like this:
select `type`.*, `category`.`table_id` as `pivot_table_id`, `category`.`type_id` as `pivot_type_id` from `type` inner join `category` on `type`.`type_id` = `category`.`type_id` where `category`.`table_id` = '23' and `category`.`table_name` = 'person';
so it seems to be correct.
But I would like to use sync() for synchronizing types with persons and here's the problem.
When I use:
$person->groups()->sync('1' => ['table_name' => 'person']);
I see the query that gets all records from category to use for sync looks like this:
select `type_id` from `category` where `table_id` = '23';
so it doesn't use
`category`.`table_name` = 'person'
condition so synchronization won't work as expected.
Is there any simple way to solve it or should I synchronize it manually?
You should use Eloquents polymorphic relations (http://laravel.com/docs/4.2/eloquent#relationships)
class Category extends Eloquent {
public function categorizable()
{
return $this->morphTo();
}
}
class Person extends Eloquent {
public function categories()
{
return $this->morphMany('Category', 'categorizable');
}
}
Now we can retrieve catgories from person:
$person = Person::find(1);
foreach ($person->categories as $category)
{
//
}
and access person or other owner from category:
$category = Category::find(1);
$categorizable_model = $category->categorizable; //e.g. Person
I can confirm that it was a bug in Laravel 5 commit I used. I've upgraded for Laravel 5 final version and now query is generated as it should.
Related
I have 2 models in my app, 'Subject' & 'Professor' (each Subject belongs to many Professors).
I made the many-to-many relation between two model using belongsToMany(). belongsToMany() doesn't work.
I'm trying to get data like this:
$subjects = Subject::with(["professors"])->whereHas("professors", function ($q){ $q->where("id", \request("professor_id")); })->get();
Error:
"SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous (SQL: select * from `subjects` where exists (select * from `professors` inner join `subjects_of_professor` on `professors`.`id` = `subjects_of_professor`.`professor_id` where `subjects`.`id` = `subjects_of_professor`.`subject_id` and `id` = 39))",
Does anyone know where did I make a mistake?
Here's the code to Models:
class Subject extends Model
{
public function professors(): BelongsToMany
{
return $this->belongsToMany(Professor::class, "subjects_of_professor");
}
}
class Professor extends Model
{
public function subjects(): BelongsToMany
{
return $this->belongsToMany(Subject::class, "subjects_of_professor");
}
}
And here is my database structure:
subjects:
id
title
subjects_of_professor:
id
subject_id
professor_id
professors:
id
name
description
I'm found mistake, i was needed to add table in my code, when i trying to get data:
$subjects = Subject::whereHas("professors", function ($q){ $q->where("professors.id", \request("professor_id")); })->get();
I have a table of articles defined by their ID, name, price and category_ID and a table of categories defined by category_ID and name.
I want to select into my controller, the list of articles, along with the category name.
How to do that?
My answer assumes you keep the models in App\Models folder
In your Articles model define the following method.
public function category()
{
return $this->belongsTo('App\Models\Category');
}
You can access it now via $myArticle->category->name;
Make sure in Categories model the correct table is defined, based on your question i can not make up the categorie table.
Put $table = 'categories'; in the category model or whatever the table name is.
with raw SQL
$query = "SELECT articles.* , categories.name AS categoryName FROM articles JOIN categories ON articles.category_ID = categories. category_ID"
$result = \DB::select(SQL);
dump($result)
or
with Eloquent you can add a method to your model to return relationship , let's call it category
class Article extends Model {
public function category(){
return $this->hasOne("App/Category" , "category_ID" , "category_ID");
}
}
now you can do this
$article = Article::find(1);
dump($article->category->name);
checkout hasOne method from the docs
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
I built this in Model
Link_to_store.php
public function store()
{
return $this->belongsTo('App\Store');
}
public function user()
{
return $this->belongsTo('App\User');
}
User.php
public function store_links()
{
return $this->hasMany('App\Link_to_store');
}
Store.php
public function user_links()
{
return $this->hasMany('App\Link_to_store');
}
I tried this query but this only joins user and link_to_store table
$personal_stores = Auth::user()->store_links->where('privilege','=','Owner');
Now I am confused how to join store table too. Can anyone help with this?
Schema is like this
Stores Table
store_id store_name
Users Table
id name
Link_to_stores Table
id store_id user_id privilege
I suppose store_links is actually a pivot table. In this case, you can use belongsToMany(), this will automatically take care of the pivot table.
To do this, in your User model you change the store function to this:
function stores() {
return $this->belongsToMany('App\Store', 'store_links', 'user_id', 'store_id')->withPivot('privilege');
}
Because the primary key of stores is not id, you will have to define this in you Store model with the following line:
protected $primaryKey = 'store_id';
Now to get the stores for a user, you simply call
$stores = Auth::user->stores()->wherePivot('privilege', 'Owner')->get();
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
You are trying to do a join here. You can do a join like this:
$stores = User::join('link_to_stores as lts', 'users.id', '=', 'lts.user_id')->join('stores as s', 'lts.store_id', '=', 's.id')->where('lts.privilege', 'Owner')->get();
But like Jerodev pointed out, it seems like Many to Many relationship might make more sense in your case. The difference is that relationship will actually execute 2 queries (1 for original model, 1 for relationship). It will then attach the related models to the original model (which is extremely handy).
I have tables:
users
- id
- name
- company_id
companies
- id
- company_name
watched_objects
- id
- user_id
- object_id
- type
Now I want to get all watched companies for a user.
So query should looks:
SELECT
companies.*
FROM companies
JOIN watched_objects ON watched_objects.object_id = companies.id
WHERE watched_objects.user_id = 1
How should I define relations?
I try this:
class User
{
public function watched()
{
return $this->hasManyThrough('App\Company', 'App\WatchedObject', 'user_id', 'id');
}
}
But query is:
SELECT
companies.*,
watched_objects.user_id
FROM companies
INNER JOIN watched_objects ON watched_objects.id = companies.id
WHERE watched_objects.user_id = 1
How I can change watched_objects.id to watched_objects.object_id.
If your treating object_id as company id, then the relation is considered to be many to many. Then table watched_objects will be the third table kept the relation of user and company.
class User {
public function watched() {
return $this->belongsToMany('App\Company', 'watched_objects', 'user_id', 'object_id');
}
}
In order to find the watched companies of user 1, you can use the following code.
$watched_companies = User::find(1)->watched;
To get all watched companies by a user you can do like this:
$watches=WatchedObject::where(['user_id'=>Auth::user()->id])->with('company')->get();
foreach($watches as $watch)
{
print_r($watch->company->company_name);
}
Relation:
Company hasMany watched objects:
public function watches()
{
return $this->hasMany('App\WatchedObject','object_id');
}
and belongsTo in watchedObject Model:
public function company()
{
return $this->belongsTo('App\Company','object_id');
}
A Venue has many Subscriptions.
A Subscription has many Subscribers (User).
Theres a pivot table, containing the relation between user_id and subscription_id.
How can I get all Subscribers from a Venue?
I have tried with:
class Venue {
/**
* Members
*/
public function members() {
return $this->hasManyThrough('App\User', 'App\Subscription');
}
}
But it fails with MySQL error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'users.subscription_id' in 'on clause' (SQL: select `users`.*, `sub
scriptions`.`venue_id` from `users` inner join `subscriptions` on `subscriptions`.`id` = `users`.`subscription_id` where `
users`.`deleted_at` is null and `subscriptions`.`venue_id` = 1)
How my Subscription model look:
`Subscription`
class Subscription extends Model {
protected $table = 'subscriptions';
/**
* Subscripers
*/
public function subscribers() {
return $this->belongsToMany('App\User');
}
/**
* Venue
*/
public function venue() {
return $this->belongsTo('Venue');
}
}
Simple question: Why are you using a third model for Subscriptions? It sounds like a normal n:m relation between User and Venue, as already written in the comments above.
class User {
public function venues() {
return $this->belongsToMany('App\Venue');
}
}
class Venue {
public function users() {
return $this->belongsToMany('App\User');
}
}
This constellation actually needs three tables, which are (i gave each model a column name):
users
- id
- name
venues
- id
- name
user_venue
- user_id
- venue_id
But to access the relations, you can simply use the Eloquent magic:
// List of all venues (as Venue models) that are in relation with User with id $id
$venues = User::find($id)->venues()->get();
// Returns the alphabetically first user that has a relation with Venue with id $id
$user = Venue::find($id)->users()->orderBy('name', 'asc')->first();
If you need to store additional information in the pivot table (e.g. when the relation has been established), you can use additional pivot fields:
user_venue
- user_id
- venue_id
- created_at
class User {
public function venues() {
return $this->belongsToMany('App\Venue')->withPivot('created_at');
}
}
class Venue {
public function users() {
return $this->belongsToMany('App\User')->withPivot('created_at');
}
}
// Returns the date of the relations establishment for the alphabetically
// first Venue the User with id $id has a relation to
$created_at = User::find($id)->venues()->orderBy('name', 'asc')->first()->pivot->created_at;
I've never tried to do whatever you are trying to do there, because it seems (with the current information) conceptually wrong. I also don't know if it is possible to set up an own model for a pivot table, but I think it should work if the pivot table has an own primary id column. It could probably be helpful if you've a third model that needs to be connected with a connection of two others, but normally that doesn't happen. So try it with pivot tables, like shown above, first.
Alright, I still don't see a good use case for this, but I can provide you a query that works. Unfortunately I wasn't able to get an Eloquent query working, but the solution should be still fine though.
class Venue {
public function members($distinct = true) {
$query = User::select('users.*')
->join('subscription_user', 'subscription_user.user_id', '=', 'users.id')
->join('subscriptions', 'subscriptions.id', '=', 'subscription_user.subscription_id')
->where('subscriptions.venue_id', '=', $this->id);
if($distinct === true) {
$query->distinct();
}
return $query;
}
}
The relation can be queried just as normal:
Venue::find($id)->members()->get()
// or with duplicate members
Venue::find($id)->members(false)->get()