Reputation: 2395
Good day! I think I am doing something wrong or I'm misunderstanding Eloquent practices.
I have three tables.
I am performing queries upon these tables using Laravel Eloquent One-To-Many-Inverse Relationship. orders.city
holds one id of cities
and orders.county
holds one id of counties
. Below is the code of my models.
App\Orders
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Orders extends Model
{
/*
* Table name
*/
protected $table = 'orders';
/*
* Get county
*/
public function county()
{
return $this -> hasOne('App\Counties', 'id', 'county');
}
/*
* Get city
*/
public function city()
{
return $this -> hasOne('App\Cities', 'id', 'city');
}
}
App\Counties
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Counties extends Model
{
protected $table = 'account_county';
public $timestamps = false;
/*
* Counties relationship
*/
public function county()
{
return $this -> belongsTo('App\Orders', 'county', 'id');
}
}
App\Cities
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Cities extends Model
{
protected $table = 'account_city';
public $timestamps = false;
/*
* City relationship
*/
public function city()
{
return $this -> belongsTo('App\Orders', 'city', 'id');
}
}
When I execute the following in Tinker: $order = App\Orders::with(['city', 'county']) -> first();
....I get the following response:
App\Orders {#3042
county: App\Counties {#3041
id: 23,
name: "Argeș",
},
city: App\Cities {#3046
id: 6924,
name: "Argeșelu",
},
}
Now when I run the following code: $order -> city
I get the following response:
"6924"
When I execute the following code however: $order -> City
I get the whole collection:
App\Cities {#3046
id: 6924,
name: "Argeșelu",
},
Why do I need to uppercase the first letter of property to get the collection instance? In documentation it states I should use the dynamic property name directly: $order -> city
however in my case this returns the raw string ID not the collection instance. Am I missing something? I am concerned about performance and good practice reasons. I apologize if it's a bad question or if it has been explained somewhere else already.
Upvotes: 0
Views: 111
Reputation: 3040
You're experiencing this problem because of the naming convention you are using for your database fields and relationships.
When loading data from a database table, will assign each field that you pull from your database to an attribute on your model. These are retrieved via PHP's __get and __set magic methods, based on the name of the database field. So $order->city
actually fetches the value of the database field on the row of that table.
One thing to note here is that city
as the attribute name is case sensitive, which brings us to the next issue.
When you call $order->City
on the model (note the case), it first looks for a database field attribute called City
. Because of case sensitivity, it cannot find a database field of that name.
The next thing Eloquent does is it checks to see if there is a relationship function called city
. Note the case. PHP class functions are not case sensitive, so you can defined a function called city
and call it as City()
. So when you ask for $order->City
, it calls the relationship function of that name and returns the DB results.
This is the reason you get the correct results when you call City
instead of city
.
To fix the issue, I recommend renaming your relationship fields city
and county
to the more standard (for Eloquent) values of city_id
and county_id
. Not only will this reduce your confusions, but you'll benefit from some of the built in Eloquent functionality to make your life easier.
As an aside to this, the reason your $order->city
was coming back as a string was due to the way Eloquent treats database fields by convention. It does not generally read the schema to look at the datatypes, but generally treats everything as strings unless you explicitly cast it to a PHP type, or it meets a specific naming convention. For instance, Eloquent will assuming any field ending in _id
is a number, and treat it as a integer by default. So renaming city
to city_id
will fix this for you automagically.
Also, check your relationships as a whole. It seems to be you would be better with BelongsTo
relationships both on City and County.
Upvotes: 1