Eloquent ORM Many-to-many relationship - php

I've just started using Eloquent ORM (Without Laravel) and I am having issues with the many to many relationships.
I have a table where I store Families (Article categories), another one for the Articles, and a third one as a "pivot". I want to be able to get all the articles a Family has, and all the families an article belongs to. So I have coded this models.
Families
class Families extends Model {
public $table = 'Families';
public function Articles() {
return $this->belongsToMany('Articles', 'articles_families', 'families_id', 'articles_id');
}
}
Articles
class Articles extends Model {
public $table = 'Articles';
public function Families() {
return $this->belongsToMany('Families', null, 'articles_id', 'families_id');
}
}
Then I am trying to retrieve the data like this:
$families = Families::all();
echo $families[1]->Articles;
However, it just returns an empty array, when it should return a couple of articles. I have tripled checked that all the values are correct in the three tables. If I echo the Eloquent query debugger I can see that it is looking for a null value and I'm pretty sure that's the problem, but I don't quite know how to fix it. Here:
{"query":"select * from `Families`","bindings":[],"time":49.13},{"query":"select `Articles`.*, `articles_families`.`families_id` as `pivot_families_id`, `articles_families`.`articles_id` as `pivot_articles_id` from `Articles` inner join `articles_families` on `Articles`.`id` = `articles_families`.`articles_id` where `articles_families`.`families_id` is null","bindings":[],"time":38.93}
The null value is at the end of the last query.

I just found the solution myself. As my primary key columns are called Id, and Eloquent by default assumes the primary key is called id, I needed to override that by adding a class property protected $primaryKey = "Id"; and it now retrieves the data properly.

Related

Eloquent ORM not fetching data using relationship laravel

I am new to laravel and using Eloquent ORM. Now the issue is I am trying to get records using relationships with with() function. Now this issue is that eloquent generates and applies the right query but does not returns any result. But if I test the same generated query on mysql than it works fine.
Following are the two tables that are involved in this:
Properties:
id , name , locality_id
Localities:
id, name , type , adjoining
Now the relationship between above mentioned tables is one to many relationship.
Property Model:
protected $table = 'properties';
protected $guarded = array('id');
public function localityAreaAndCity() {
return $this->belongsTo('Locality','locality_id')
->leftjoin('localities as ls', function($join)
{
$join->on('localities.id', '=', 'ls.adjoining')
->where('localities.type', '=','area');
});
->select(array('localities.name as localityPrimaryName',
'localities.type as localityPrimaryType',
'ls.name as localitySecondaryName',
'ls.type as localitySecondaryType'));
}
Locality Model:
public $timestamps = false;
protected $table = 'localities';
protected $guarded = array('id');
public function properties()
{
return $this->hasMany('Property');
}
Eloquent Query:
$properties = Property::with('localityAreaAndCity')->get();
DB::getQueryLog() Result:
select `localities`.`name` as `localityPrimaryName`, `localities`.`type` as `localityPrimaryType`, `ls`.`name` as `localitySecondaryName`, `ls`.`type` as `localitySecondaryType` from `localities` left join `localities` as `ls` on `localities`.`id` = `ls`.`adjoining` and `localities`.`type` = ? where `localities`.`id` in (?, ?, ?, ?, ?)
Know if I use the above mentioned query in mysql then it returns data but using with Eloquent ORM it returns NULL.
Please Help..
Just added locality_id to the select and it worked. Actually eloquent works like you have 2 arrays with related items and you want to match them - if there's no foreign key in one of them, that points to the primary key in the other one, then you can't do that. Eloquent does exactly the same.
You must always select both keys of the relation (most likely PK on one model and FK on the other one).

Laravel belongsToMany relationship defining local key on both tables

So the belongsToMany relationship is a many-to-many relationship so a pivot table is required
Example we have a users table and a roles table and a user_roles pivot table.
The pivot table has two columns, user_id, foo_id... foo_id referring to the id in roles table.
So to do this we write the following in the user eloquent model:
return $this->belongsToMany('Role', 'user_roles', 'user_id', 'foo_id');
Now this looks for an id field in users table and joins it with the user_id field in the user_roles table.
Issue is I want to specify a different field, other than id to join on in the users table. For example I have bar_id in the users table that I want to use as the local key to join with user_id
From laravel's documentation, it is not clear on how to do this. In other relationships like hasMany and belongsTo we can specify local key and foriegn key but not in here for some reason.
I want the local key on the users table to be bar_id instead of just id.
How can I do this?
Update:
as of Laravel 5.5 onwards it is possible with generic relation method, as mentioned by #cyberfly below:
public function categories()
{
return $this->belongsToMany(
Category::class,
'service_categories',
'service_id',
'category_id',
'uuid', // new in 5.5
'uuid' // new in 5.5
);
}
for reference, previous method:
I assume id is the primary key on your User model, so there is no way to do this with Eloquent methods, because belongsToMany uses $model->getKey() to get that key.
So you need to create custom relation extending belongsToMany that will do what you need.
A quick guess you could try: (not tested, but won't work with eager loading for sure)
// User model
protected function setPrimaryKey($key)
{
$this->primaryKey = $key;
}
public function roles()
{
$this->setPrimaryKey('desiredUserColumn');
$relation = $this->belongsToMany('Role', 'user_roles', 'user_id', 'foo_id');
$this->setPrimaryKey('id');
return $relation;
}
On Laravel 5.5 and above,
public function categories()
{
return $this->belongsToMany(Category::class,'service_categories','service_id','category_id', 'uuid', 'uuid');
}
From the source code:
public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
$parentKey = null, $relatedKey = null, $relation = null)
{}
This is a recently added feature. I had to upgrade to 4.1 because I was also looking for this.
From the API documentation:
public BelongsToMany belongsToMany(string $related, string $table = null, string $foreignKey = null, string $otherKey = null, string $relation = null)
The $otherKey and $relation parameters were added in 4.1. Using the $foreignKey and $otherKey parameters allows you to specify the keys on both sides of the relation.
The best way is set the primary key.
class Table extends Eloquent {
protected $table = 'table_name';
protected $primaryKey = 'local_key';
belongsToMany allows to define the name of the fields that are going to store che keys in the pivot table but the method insert always the primary key values into these fields.
You have to:
define in the method belongsToMany the table and the columns;
then using protected $primaryKey = 'local_key'; you can choose which value store.
I recently went through the same problem where I needed to have an associated table that used ID's to link two tables together that were not Primary Keys. Basically what I did was create a copy of my model that models the pivot table and set the Primary Key to the value that I wanted it to use. I tried creating a model instance, settings the primary key and then passing that to the relation but Laravel was not respecting the primary key I had set ( using the ->setPrimaryKey() method above ).
Making a copy of the model and setting the primary key feels a little bit 'hackish' but in the end it works as it should and since Pivot table models are generally very small I don't see it causing any problems in the future.
Would love to see a third key option available in the next release of Laravel that lets you get more specific with your linking.

Eloquent ORM query -- newbie

I am just starting with ORM. I had a question, this is my tabel --
table a - (aid, aname, atag);
table b - (bid, aid, bname, .. );
It is One to Many relationship - that is One aid can belong to many bid but one bid can belong to only one aid.
So I was trying out this code, In the out put I want -- (bname,aname) for all the records.
A model --
class A extends Eloquent {
protected $table = 'a';
protected $primaryKey = 'aid';
public function brelation() {
$this->belongsToMany('B','aid');
}
}
B model --
class B extends Eloquent {
protected $table = 'b';
protected $primaryKey = 'bid';
public function getANames() {
$this->hasOne('A','aid');
}
}
In Controller --
foreach(B::with('getANames')->get() as $b_item){
echo $b_item->bname." , ".$b_item->aname;
}
Couple of points to clarify --
1) I have to specify the foreign key to make sure they map. Because in my actual case they are named differently.
2) I am using Laravel 4.
Can someone show me what I did wrong and how I can get the desired result.
===== Update =====
class A extends Eloquent {
protected $table = 'a';
protected $primaryKey = 'aid';
public function brelation() {
$this->belongsTo('B','aid');
}
}
I still cannot access the aname column i.e ($b_item->aname) in the controller.
A couple of things you should be aware of:
If you have a custom primary key, you need to set the $primaryKey property on your eloquent models to the primary keys you have in the DB.
You can't mix and match belongsToMany relationships with anything other than belongsToManys. A belongsToMany is exclusively for the case where you have two tables that are connected by a pivot table. In your case, B belongsTo A, and A hasMany B.

Laravel/Eloquent - Eager Load using other than Primary Key

Eager Loading uses Primary Keys from the parent table inside the IN(...) clause, as clearly stated in Laravel's documentation:
select * from books
select * from authors where book_id in (1, 2, 3, 4, 5, ...)
Is it possible not to use the Primary Key for this, but another key from the books table? Or even a key retrieved from a pivot table?
Yeah we can do eager loading on non-primary columns. For this you need to define db-data relationships in models:-
Define your models Book & Author like this:-
class Books extends Eloquent {
public static $table = 'books';
public function bookauthors(){
return $this->has_many('authors','book_id');
}
}
class Authors extends Eloquent {
public static $table = 'authors';
public function books()
{
return $this->belongs_to('books','book_id');
}
}
Now you can use following line of code to eager load books with authors on a key "book_id":-
$books = Books::with(array('bookauthors'))->get();
I hope this helps you...i haven't tested on my local or test db. But had used similar eager loading for posts-tags relationship.

Laravel 4: Get all product attributes with relationship table

I've got the following tables:
products (
id
)
attributes (
id
name
value
)
products_attributes (
product_id,
attribute_id
)
And I need to be able to query for all of the attributes of a specific product. I tried doing this with the FLUENT QUERY BUILDER but I'm getting lost in my own code.
Can someone help me out with an example?
Usually you would create models for both of your entities, in which you can specify the relationships:
class Product extends Eloquent
{
protected $table = 'products';
public function attributes()
{
return $this->belongsToMany('Attribute', 'products_attributes');
}
}
class Attribute extends Eloquent
{
protected $table = 'attributes';
public function products()
{
return $this->belongsToMany('Product', 'products_attributes');
}
}
The belongsToMany() method sets up a many-to-many relationship. The first parameter specifies the related model class name, the second one the name of the database table that holds the connections between the two entities.
To find a product with ID 1234, you would fetch it like this:
$product = Product::find(1234);
You can then magically access all of its attributes like this:
$attributes = $product->attributes;
For more information, you can refer to the documentation.

Categories