many-to one relationship Eloquoent Orm - php

I have a challenge with Eloquent ORM for laravel am actually a newbie into this Eloquoent orm .i have a Model Country and a Model State. The state table has a foreign_key countryId. The issue is if i fetch all the states using eager loading with relationship as follows.
$countries = State::with('country')->get();
and my State model is as follows
class State extends Model
{
protected $fillable = ['name', 'countryId'];
protected $table = 'states';
function Country(){
return $this->belongsTo('Country','id');
}
}
The result i get is as follows
[
{
"id": 1,
"name": "Lagos",
"countryId": "1",
"created_at": "2017-03-09 13:09:12.000",
"updated_at": "2017-03-09 13:09:12.000",
"country":{"id": 1, "name": "Nigeria", "created_at": "2017-03-09 10:55:46.000", "updated_at": "2017-03-09 10:55:46.000"…}
},
{
"id": 2,
"name": "Oyo",
"countryId": "1",
"created_at": "2017-03-09 13:29:12.000",
"updated_at": "2017-03-09 13:29:12.000",
"country": null
}
]
it can bbe seen from the json output that the second item country object is not loaded.
i also tried to set a relationship on the Country model but no changes
class Country extends Model
{
protected $fillable = ['name'];
protected $table = 'countries';
function State(){
return $this->hasMany('State', 'countryId');
}
}
thanks in anticipation of your response

Try to fix your relationship first and try again. Define it this way in State model
public function country(){ //lower 'c' NOT capital
return $this->belongsTo(Country::class, 'countryId'); //Pass the class AND the foreign key. You passed the primary key
}
Then try again.
The reason the second one has no country is because you set 'id' in the relationship and there is no country with 'id' 2. So simply fix your relationship and you should be good to go.

Related

Laravel appends only one attribute from relationship

I'm trying to add an extra field to my Laravel 7 API model:
class Layer extends Model {
protected $fillable = ['name', 'filename', 'status', 'category_id'];
protected $appends = ['category_name'];
public function getCategoryNameAttribute()
{
return $this->category->name;
}
public function category()
{
return $this->belongsTo('App\LayerCategory', 'category_id');
}
}
My Controller:
.....
public function index()
{
return Layer::orderBy('name', 'asc')->paginate();
}
.....
I expect the following response:
{
"id": 1,
"category_id": 1,
"name": "My Layer",
"filename": "file_name.zip",
"status": true,
"category_name": "My category name"
}
But I receive:
{
"id": 1,
"category_id": 1,
"name": "My Layer",
"filename": "file_name.zip",
"status": true,
"category_name": "My category name",
"category": [
"id": 1,
"name": "My category name",
"status": true
]
}
How to return only the category name?
PS: I also tried with Resources.
Since you're eager loading the Category relationship to get category_name, you'll need to add logic to hide category from your JSON response. This can be done using the Hidden attribute on Serialization:
protected $hidden = ['category'];
You can read https://laravel.com/docs/7.x/eloquent-serialization#hiding-attributes-from-json for more information.

Load All posts with their tags into an array Laravel eloquent polymorphic relationship

I need to fetch all posts existed in my table with all their tags into an array. As you see below, this query just gets the posts that have tags but I need all posts even they don't have any tags. In addition, I need to have a tag:[] variable in my array but I don't know how to get all posts with their tags in one query.
Thank you in advance.
public function getItemsWithTags(array $attr)
{
return self::$model::
whereNotNull('title')->
whereNotNull('content')->
whereNotNull('author')->
whereNotNull('category')->
whereNotNull('img_url')->
has('tags')->
get()->
toArray();
}
class BaseModel extends Model
{
protected $table;
protected $primaryKey;
use SoftDeletes;
}
class BlogModel extends BaseModel
{
protected $table = 'blog';
protected $primaryKey = 'id';
public function tags()
{
return $this->morphToMany(TagsModel::class,'taggable',null,null,'tag_id');
}
}
class TagsModel extends BaseModel
{
protected $table = 'tags';
protected $primaryKey = 'id';
protected $fillable = ['name'];
public function blog()
{
return $this->morphedByMany(BlogModel::class,"taggable");
}
}
the Output of this query:
{
"id": 28,
"title": "dfg",
"content": "<ul>\n<li>rdgrdgf</li>\n<li>dfg</li>\n<li>dg</li>\n<li>dgf</li>\n<li>dfg</li>\n</ul>",
"author": "gd",
"category": "Design",
"img_url": "uploads/post/Screenshot from 2020-03-27 20-34-42_7dc41dca1ebc4dabcb921fc7f4c4744a.png",
"created_at": "2020-05-03T18:47:38.000000Z",
"updated_at": "2020-05-03T18:47:38.000000Z",
"deleted_at": null,
"user_id": 1,
"category_id": 7
}
and what I need is :
{
"id": 28,
"title": "dfg",
"content": "<ul>\n<li>rdgrdgf</li>\n<li>dfg</li>\n<li>dg</li>\n<li>dgf</li>\n<li>dfg</li>\n</ul>",
"author": "gd",
"category": "Design",
"img_url": "uploads/post/Screenshot from 2020-03-27 20-34-42_7dc41dca1ebc4dabcb921fc7f4c4744a.png",
"created_at": "2020-05-03T18:47:38.000000Z",
"updated_at": "2020-05-03T18:47:38.000000Z",
"deleted_at": null,
"user_id": 1,
"category_id": 7,
"tags":['tag1','tag2']
}
return self::$model::
with('tags')->
whereNotNull('title')->
whereNotNull('content')->
whereNotNull('author')->
whereNotNull('category')->
whereNotNull('img_url')->
get()->
toArray();
You use has as the name tells, this method checks if there is a tag relationship, use with instead:
public function getItemsWithTags(array $attr)
{
return self::$model::
whereNotNull('title')->
whereNotNull('content')->
whereNotNull('author')->
whereNotNull('category')->
whereNotNull('img_url')->
with('tags')->
get()->
toArray();
}

Laravel Relationship between 3 tables

so I am trying to get the category name and this is what I have
tablets:
categories: id,name
post_categories: id, post_id, category_id
post : id, columns
I have the models: Post, PostCategory and Category.
Post Model :
public function categories()
{
return $this->hasMany('App\PostCategory');
}
PostCategory Model :
public function category()
{
return $this->hasMany(Category::class, 'id', 'category_id');
}
and in the controller I have
return Post::with('categories.category')->orderby('id','desc')->get();
And my result is
[
{
"id": 50,
"categories": [
{
"id": 92,
"category_id": 11,
"category": [
{
"id": 11,
"name": "JXrfLHdQVNON",
"image": null
}
]
}
]
}
]
and i wanted it to be something like
[
{
"id": 50,
"categories": [
{
"id": 1,
"name": "category_name",
"image": null
}
]
}
]
Is there a way to do it? I have been playing around with this and havent manage to find a easy way to do it
inside your Post model :
public function categories()
{
return $this->belongsToMany('App\Model\Category','post_categories','post_id','category_id')->withTimestamps();
}
in your controller :
return Post::with('categories:id,name,image')->orderBy('id','desc')->get();
This is a many-to-many relationship. Make sure you have your relationship set up correctly. If you want to choose specific columns you can do:
return Post::with('categories.category:id,name')->orderby('id','desc')->get();
It will return the columns id, name from category table/ model. You must specify id doing that.
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many
https://laravel.com/docs/5.7/eloquent-relationships#eager-loading

Laravel/Eloquent mutate model on join

I'm trying to mutate a model to display an array of strings and not an array of objects.
Current Output
{
"id": 1,
"company_name": "blah"
"language": [
{
"name": "English"
},
{
"name": "French"
}
]
},
{
"id": 2,
"company_name": "blah2"
"language": [
{
"name": "English"
},
{
"name": "French"
}
]
}
Desired Output
{
"id": 1,
"company_name": "blah"
"language": ["English","French"]
},
{
"id": 2,
"company_name": "blah2"
"language": ["English","French"]
}
Partner Model
class Partner extends Eloquent
{
protected $table = 'property_managers';
protected $visible = array('id','company_name','language');
public function language(){
return $this->belongsToMany('Language', 'property_manager_language', 'property_manager_id', 'language_id');
}
}
Language Model
class Language extends Eloquent
{
protected $table = 'languages';
protected $visible = array('name');
public function propertyManager()
{
return $this->belongsToMany('PropertyManager');
}
}
I'm accessing it via the code snippet below
$pm = Partner::with('language')->get();
I looked into mutators but it doesn't seem to hit that attribute within the partner model.
public function getLanguageAttribute(){
//run my code to flatten to array
}
So I'm curious as to if mutators don't work on joins or how you would be able to modify the return on the join. I can add a mutator to the Language model but that will only modify the attributes of that specific model. Any insight would be helpful. Thanks
In your model,
public function getLanguagesAttribute(){
return array_pluck(collect($this->language)->toArray(), 'name');
}
luego prueba con esto
$partner = Partner::find(1);
return $partner->languages;

Eloquent hasOne Relationship Returns NULL even when related records exist

I have a small practice accounting app that I'm building using Laravel to provide a REST api. I am trying to use eloquent relationships to handle delivering objects including their relations.
Right now I have a "Transaction" model which looks like the following.
class Transaction extends Model
{
protected $table = "transaction";
protected $fillable = [
"transaction_type_id",
"account_id",
"description",
"amount",
"comments",
"transaction_status_id",
"posting_date"
];
protected $guarded = [
"id"
];
public static function create(array $attributes = Array()) {
$account = BankAccount::find($attributes["account_id"]);
if ($attributes["transaction_type_id"] == 1) {
//Credit
$account["balance"] += $attributes["amount"];
} else if ($attributes["transaction_type_id"] == 2) {
//Debit
$account["balance"] -= $attributes["amount"];
}
$account->save();
parent::create($attributes);
}
public function transactionType() {
return $this->hasOne("App\Model\TransactionType", "id");
}
public function transactionStatus() {
return $this->hasOne("App\Model\TransactionStatus", "id");
}
}
The TransactionStatus model looks like this
class TransactionStatus extends Model
{
protected $table = "transaction_status";
protected $guarded = [
"id", "description"
];
}
And the TransactionType model looks like this
class TransactionType extends Model
{
protected $table = "transaction_type";
protected $guarded = [
"id", "description"
];
}
I have the following in my controller
return Transaction::with("TransactionType", "TransactionStatus")
->where("account_id", $accountId)
->get()
->toJson();
This query returns a result that looks like the following, where the first result returns the relation, but every subsequent record has null.
{
"id": 1,
"transaction_type_id": 1,
"account_id": 1,
"description": "Morbi metus. Vivamus euismod urna.",
"amount": "4179.00",
"comments": "ullamcorper, nisl arcu iaculis enim,",
"transaction_status_id": 1,
"posting_date": "2016-12-10 21:24:25",
"created_at": "2016-12-10 00:00:00",
"updated_at": "2016-12-10 00:00:00",
"transaction_type": {
"id": 1,
"description": "Credit",
"created_at": "2016-12-09 13:37:00",
"updated_at": "2016-12-09 13:37:00"
},
"transaction_status": {
"id": 1,
"description": "Pending",
"created_at": "2016-12-09 13:37:00",
"updated_at": "2016-12-09 13:37:00"
}
},
{
"id": 3,
"transaction_type_id": 1,
"account_id": 1,
"description": "lorem ut aliquam iaculis, lacus",
"amount": "2710.00",
"comments": "ac, eleifend vitae, erat. Vivamus",
"transaction_status_id": 1,
"posting_date": "2016-07-16 04:23:34",
"created_at": "2016-12-10 00:00:00",
"updated_at": "2016-12-10 00:00:00",
"transaction_type": null,
"transaction_status": null
}
The above result makes no sense to me because all of the records in the transaction table have both a transaction_type_id and a transaction_status_id, so these shouldn't be null at all.
I've looked high and low trying to understand why this might happen, but I've not been able to find an explanation. I've included a screenshot below that indicates how the records look in the table in MySQL as well.
I found the solution to the problem and it was along the same lines of what IzzEps pointed out.
The way that I had the relationships originally set up is like this.
public function transactionType() {
return $this->hasOne("App\Model\TransactionType", "id");
}
public function transactionStatus() {
return $this->hasOne("App\Model\TransactionStatus", "id");
}
I took another look at the eloquent relationship docs and found this:
Additionally, Eloquent assumes that the foreign key should have a value matching the id (or the custom $primaryKey) column of the parent. In other words, Eloquent will look for the value of the user's id column in the user_id column of the Phone record. If you would like the relationship to use a value other than id, you may pass a third argument to the hasOne method specifying your custom key: return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
When I changed the relationships to this:
public function transactionType() {
return $this->hasOne("App\Model\TransactionType", "id", "transaction_type_id");
}
public function transactionStatus() {
return $this->hasOne("App\Model\TransactionStatus", "id", "transaction_status_id");
}
I got the results I was expecting. In this case "id" is the foreign key which exists on the transaction_status or transaction_type table, and "transaction_type_id" or "transaction_status_id" is the key that is "local" to the "transaction" table or the "local_key" in the parlance of the docs.
I was not fully specifying how the relationships are defined and what keys the relationships should be using. Thanks for pointing me in the right direction which, as should be second-nature, was back to the documentation.
So, as always, I should more closely RTM.
When defining the following relationships
public function transactionType() {
return $this->hasOne("App\Model\TransactionType", "id");
}
public function transactionStatus() {
return $this->hasOne("App\Model\TransactionStatus", "id");
}
you are specifying the id of the Transaction model table as the foreign key for both of these relationships (as opposed to the correct correct foreign key's of transaction_type_id and transaction_status_id respectively). The result is that when calling Transaction::with("TransactionType", "TransactionStatus") you are joining both the transaction_type and transaction_status table on the wrong foreign key (transaction.id).
To Fix:
Simply update your relationships to use the correct foreign keys as follows:
public function transactionType() {
return $this->hasOne("App\Model\TransactionType", "transaction_type_id");
}
public function transactionStatus() {
return $this->hasOne("App\Model\TransactionStatus", "transaction_status_id");
}
Hope this helps...
Didn't see it the first time. Actually you are storing the transaction_type_id and transaction_status_id on your `Transaction model. Which essentially means that the relation should be defined as
Transaction belongsTo TransactionType & (Inverse) TransactionType hasMany Transaction
and
Transaction belongsTo TransactionStatus & (Inverse) TransactionStatus hasMany Transaction
The signatures of hasOne, belongsTo and hasMany are very different.
So the relationships should be defined (as per your current code)
class Transaction extends Model
{
protected $table = "transaction";
protected $fillable = [
"transaction_type_id",
"account_id",
"description",
"amount",
"comments",
"transaction_status_id",
"posting_date"
];
public function transactionType()
{
return $this->belongsTo('App\Model\TransactionType');
}
public function transactionStatus()
{
return $this->belongsTo('App\Model\TransactionStatus');
}
}
class TransactionType extends Model
{
protected $table = "transaction_types";
protected $fillable = [ /* mass assignable fields */];
public function transactions()
{
return $this->hasMany('App\Model\Transaction');
}
}
class TransactionStatus
{
protected $table = "transaction_statuses";
protected $fillable = [ /* mass assignable fields */];
public function transactions()
{
return $this->hasMany('App\Model\Transaction');
}
}
As per Laravel documentation Eloquent determines the foreign key of the relationship based on the model name.
Since you are following desired convention of naming the foreign key corresponding to the Model names
transaction_type_id <=> TransactionType and transaction_status_id <=> TransactionStatus
you can omit specifying the foreign key as second argument to the relationship function.
After defining the relationships as above
return Transaction::with('transactionType', 'transactionStatus')
->where("account_id", $accountId)
->get()
->toJson();
should return the desired output for you. Haven't tested but should definitely work.
Hope this helps. Let me know if otherwise.
public function dispute() {
return $this->hasOne('App\PaypalDispute', 'transaction_id', 'transaction_id');
}
I managed to get it like this. We should use the local_key as the column name in order to work.

Categories