Property [countries] does not exist on this collection instance - laravel

I am trying to create a drop down menu by with eloquent from where I can go to subcontinent with drop down and subcontinent to countries with sub-dropdown. The relationship is Subcontinent has many countries.
Models
Subcontinent
class Subcontinent extends Model
{
protected $guarded = [];
public function countries()
{
return $this->hasMany(Division::class, 'country_name', 'id');
}
}
Country
class Division extends Model
{
protected $table = 'divisions';
protected $fillable = [
'country_name', 'subcontinent_id'
];
public function subcontinent()
{
return $this->belongsTo(Subcontinent::class, 'country_name', 'id');
}
}
The table name of country is divisions and the model name is also Division.
Table
country/division
Schema::create('divisions', function (Blueprint $table) {
$table->id();
$table->string('country_name');
$table->bigInteger('subcontinent_id');
$table->timestamps();
});
Database formation
$subcontinents = Subcontinent::orderBy('id', 'DESC')->get();
But when I try to call dd($subcontinents->countries) it gives me property does not exist error.
"Property [countries] does not exist on this collection instance."
with $subcontinents = Subcontinent::find(1);
the dd still gives null value. How can I call subcontinents to countries!

you have misconception about second and third option in relationship method. for belongsTo relationship, the second argument is the foreign key of the child table and the third argument is the primary key or the reference key of the parent table.
your Division model relationship should be
public function subcontinent()
{
return $this->belongsTo(Subcontinent::class, 'subcontinent_id', 'id');
}
and for hasMany relationship the second argument is the foreign key in the child table. For SubContinent model the relationship would be
public function countries()
{
return $this->hasMany(Division::class, 'subcontinent_id', 'id');
}
and when you use $subcontinents = Subcontinent::orderBy('id', 'DESC')->get(); you get a collection, not an object. you have to loop over to get values and relationship data from this.
foreach($subcontinents as $subcontinent) {
$subcontinent->$subcontinent_name;
$subcontinent->countries;
}
and when you use $subcontinents = Subcontinent::find(1); you get an object. you can directly access its values. just update the relationship method. and you will get values by $subcontinents->countries then.

Related

Define additional relationship on many-to-many polymorphic relationship

I'm creating an taggable table like so:
Schema::create('taggable', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tag_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
$table->unsignedBigInteger('company_id');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
$table->unsignedBigInteger('created_by')->nullable();
$table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
$table->timestamps();
});
As you can see, next to connecting tags to a Post, Video etc (as per the Laravel docs example), I'd also like to ensure that the row that's added is connected to a Company and User model so I can keep track who it belongs to and who created it, but even more so access properties from those models in controllers and views.
I know that in my Post model I can do:
public function tags()
{
return $this->morphedByMany(\App\Models\Tag::class, 'taggable')->withPivot('created_by', 'company_id', 'created_at');
}
The problem is that this will retrieve just the value for created_by and company_id and not the Eloquent model. Is this possible?
So what I'd like to do is access properties of those relationships in controllers and views like so:
$post = Post::findOrFail(1);
foreach($post->tags as $tag) {
$tag->created_by->name // accessing 'name' property on the `User` model
}
foreach($post->tags as $tag) {
$tag->company->address // accessing `address` property on the `Company` model
}
You must do like below:
first you must define relationship between tags and users
class Tags extends Model
{
public function taggable(): MorphTo
{
return $this->morphTo();
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}
then for achieve that you want you must:
$post = Post::first();
$users = $post->tags()->with('createdBy')->get();

Eloquent 'with()' returns null

I have a simple relationship between two models: User and Prescription. A user has many prescriptions. In the PrescriptionsController, when I try to get the user that the prescriptions belongs to, it returns null when using with().
PrescriptionsController
public function index()
{
$user_id = Auth::id();
$prescriptions = Prescription::with('user')->where('prescription_for', $user_id)->get();
return response()->json($prescriptions);
}
The result from that Eloquent query:
[{"id":1,"prescription_for":1,"prescription_by":1,"prescription_content":"Paracetamol 120mg - O cutie. Mod administrare: 1 Dimineata | 0 Pranz | 0 Seara","created_at":"2020-10-13T17:33:35.000000Z","updated_at":null,"user":null}]
You can see that the last parameter is null.
In my User model, I have set up the relationship using:
public function prescriptions()
{
return $this->hasMany(Prescription::class);
}
And the Prescription model:
protected $table = 'prescriptions';
public $primaryKey = 'id';
public $timestamps = true;
public function user()
{
return $this->belongsTo(User::class);
}
I am using VueJs so I cannot just do $prescription->user->name as you can in Blade files, that's why I need to eager load the data.
The way I set up the Prescriptions table:
$table->id();
$table->unsignedBigInteger('prescription_for');
$table->foreign('prescription_for')->references('id')->on('users');
$table->unsignedBigInteger('prescription_by');
$table->foreign('prescription_by')->references('id')->on('users');
$table->string('prescription_content');
$table->timestamps();
Any ideas to why this happens? Thanks!
On your prescriptions table, your primary key is prescription_for. If the parent model does not use id as its primary key, or you wish to find the associated model using a different column, you may pass a third argument to the belongsTo() method specifying the parent table's custom key :
public function user()
{
return $this->belongsTo(User::class, 'id', 'prescription_for');
}

Can't access pivot model's id attribute inside of the static::saved() method in Laravel

I can't access pivot model's id attribute. I have one pivot model PivotModel and two models that are connected through this pivot model
ModelA class:
public function modelB()
{
return $this->belongsToMany(ModelB::class, 'model_a_model_b', 'model_a_id', 'model_b_id')
->using(PivotModel::class)
->withPivot('id', 'prop_1', 'prop_2');
}
ModelB class:
public function modelA()
{
return $this->belongsToMany(ModelA::class, 'model_a_model_b', 'model_b_id', 'model_a_id')
->using(PivotModel::class)
->withPivot('id', 'prop_1', 'prop_2');
}
PivotModel:
use Illuminate\Database\Eloquent\Relations\Pivot;
class PivotModel extends Pivot
{
public $incrementing = true;
public static function boot() {
parent::boot();
static::saved(function ($model) {
dump($model->id);
dump($model->toArray());
});
}
}
Pivot table migration file
Schema::create('model_a_model_b', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('model_a_id');
$table->unsignedInteger('model_b_id');
$table->string('prop_1');
$table->string('prop_2');
$table->unique(['model_a_id', 'model_b_id'], 'model_a_id_model_b_id');
$table->foreign('model_a_id')
->references('id')->on('model_a')
->onDelete('cascade')
;
$table->foreign('model_b_id')
->references('id')->on('model_b')
->onDelete('cascade')
;
$table->timestamps();
});
I assume this should work.
This is from the official documentation for Laravel 5.8
Custom Pivot Models And Incrementing IDs
If you have defined a many-to-many relationship that uses a custom pivot model, and that pivot model has an auto-incrementing primary key, you should ensure your custom pivot model class defines an incrementing property that is set to true.
/**
* Indicates if the IDs are auto-incrementing.
*
* #var bool
*/
public $incrementing = true;
I can only access the prop_1 and prop_2 properties but not the id property.
The id is null
dump($model->id);
and the toArray() only shows other props but not the id
dump($model->toArray());
I found a temporary solution. If you know how to do it better please suggest.
As mentionied, the id property is accessible in the created() method
so you can easily get it using $model->id.
static::created(function ($model) {
dump($model->id);
});
The problem is in the updated() method where the $model instance is filled with properties other than id.
See the method updateExistingPivotUsingCustomClass inside of Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithPivotTable
static::updated(function($model) {
// If the model is of type Pivot we have to store it into a different variable or overwrite it. If we need dirty props we first need to store them to separate variable before overwriting the $model variable
$dirtyProps = $model->getDirty();
if($model instanceof Pivot) {
$model = get_class($model)
::where($model->foreignKey, $model->{$model->foreignKey})
->where($model->relatedKey, $model->{$model->relatedKey})
->firstOrFail();
// Use the model
$model->id ...
}
});

I am try to add relationship in Laravel. It self join one to many relationship

I am try to get record e.g category table parent category related to sub category list and sub category inside to other sub category list in sort number of levels in sub category
I am try to create relation ship in model class but not work I sort data are not get in relationship function. Check this code and replay
I am try this relationship in model class
public function sub_category()
{
return $this->belongsTo(self::class, 'parent_id', 'id');
}
public function parent_category()
{
return $this->hasMany(self::class, 'id', 'parent_id');
}
But not work as my expectation.
Not get any data.
call this type of query
$category_data = Category::where('parent_id',0)
->get();
$category_data->sub_category;
but error is undefined function sub_category
also try this way
$category_data->sub_category();
last I am create function
Let me try this way as custom create function to manage array
public function sub_cat($data)
{
# code...
foreach ($data as $key => $value) {
# code...
$sub_category = $this->where('parent_id',$value->id)->get();
$sub = $sub_category;
if(!empty($sub)){
$sub = $this->sub_cat(json_decode($sub));
$value->sub = $sub;
}
}
return $data;
}
this type function work.
but my expectation to get data only add relationship
I am expected only use relationship to create array as parent category, child category, sub child category, sub to sub child category.
I got it to work. I Imagine we have a Category model with the following attributes (at least).
# Migration file
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('parent_id');
});
# Model file
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $table = 'categories';
public function children()
{
// https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php#L325
return $this->hasMany(self::class, 'parent_id', 'id');
}
public function parent()
{
// https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php#L170
return $this->belongsTo(self::class, 'parent_id', 'id');
}
}
$category = Category::find($id);
// parent category
$category->parent
// child categories
$category->children
Also, you're using get() to get your category. get() returns a Collection instance.
# Instead of
Category::where('parent_id',0)->get(); // returns a Collection of Category models
# try
Category::where('parent_id',0)->first(); // returns a Category model
# or
Category::where('parent_id',0)->get()->first(); // returns first item of Collection of Category models

How to properly implement this polymorphic relationship in Laravel?

I am trying to build an inventory for users in Laravel 5.8, however the items have their own properties, therefore I needed to set up a polymorphic relationship. When attaching items to users, it tries to add the model User to the table on itemable_type and the user's ID to itemable_id aswell as add the User's ID to user_id, something I could workaround by passing the models I need, but when I try to retrieve them it tries to find item with itemable_type = 'App\Models\User', which makes me think something's completely wrong here. Can I have some orientation on how to solve it?
class User extends Model
{
public function inventory()
{
return $this->morhpToMany(InventoryItem::class, 'itemable', 'user_inventories', null, 'itemable_id')
->withPivot('amount', 'notes');
}
}
class InventoryItem extends Model
{
public $timestamps = false;
protected $table = 'character_inventories';
protected $fillable = [
'character_id', 'itemable_type', 'amount', 'parent_id', 'notes'
];
public function cloth()
{
return $this->mophedByMany(Cloth::class, 'itemable');
}
public function food()
{
return $this->morphedByMany(Food::class, 'itemable');
}
// Other similar relations
}
// The Inventory migration:
Schema::create('user_inventories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->unsignedInteger('itemable_id');
$table->string('itemable_type');
$table->unsignedInteger('amount')->default(0);
$table->text('notes', 65535)->nullable();
$table->foreign('character_id')->references('id')->on('characters');
});
The expected result is the User model to have different items in his inventory, but the relation is trying to query by joinning to itself and filtering by user type instead of actual items.
The error:
Syntax error or access violation: 1066 Not unique table/alias: 'user_inventories' (SQL:
select `user_inventories`.*,
`user_inventories`.`itemable_id` as `pivot_itemable_id`,
`user_inventories`.`itemable_type` as `pivot_itemable_type`,
`user_inventories`.`amount` as `pivot_amount`,
`user_inventories`.`parent_id` as `pivot_parent_id`,
`user_inventories`.`notes` as `pivot_notes`
from `user_inventories`
inner join `user_inventories` on `user_inventories`.`id` = `user_inventories`.`itemable_id`
where `user_inventories`.`itemable_id` in (4)
and `user_inventories`.`itemable_type` = App\Models\User)
I highly suspect that you have to references the user table in the inventory relation. In general it is a million times easier just following the Laravel convention for naming.
public function inventory()
{
return $this->morhpToMany(InventoryItem::class, 'itemable', 'users', null, 'itemable_id')
->withPivot('amount', 'notes');
}

Resources