belongsToMany in v7 returns an empty array - laravel

My client has an old site built on 4.x that I'm trying to get to work with 7.4. I have most of it working, but am stuck on a belongsToMany relationship
I have a Manufacturer class that has a many-to-many relationship with Subcategories, through a table named membercategory. However, the subcategories property always returns an empty array. What am I missing?
membercategory
+------+------------------+-----------------+
| ID | FKManufacturerID | FKSubCategoryID |
+------+------------------+-----------------+
| 3203 | 24 | 301 |
| 3202 | 24 | 292 |
| 3201 | 24 | 295 |
+------+------------------+-----------------+
and my Manufacturer class
class Manufacturer extends Model {
public function subcategories() {
# have tried swapping the two FK parameters, same result
return $this->belongsToMany('App\Subcategory','membercategory','FKSubCategoryID','FKManufacturerID');
}
}
I'm testing it in my controller using this
dd($manufacturers[0]->subcategories);
where $manufacturers[0] returns the object for Manufacturer ID=24

According to the documentation, your code should look like this:
class Manufacturer extends Model {
public function subcategories() {
return $this->belongsToMany('App\Subcategory','membercategory','FKManufacturerID','FKSubCategoryID');
}
}
Looking at the belongsToMany signature:
public function belongsToMany(
$related,
$table = null,
$foreignPivotKey = null,
$relatedPivotKey = null,
$parentKey = null,
$relatedKey = null,
$relation = null) {}
You will see that you need to have the related column in the 4th param and the foreign one in the 3rd.
EDIT
Make sure the types of columns in the model tables and the pivot table match.
Furthermore, according to the belongsToMany signature, you should add the parentKey and relatedKey params if they are not the default: id ( ID != id )
Here is a list of eloquent model conventions in Laravel 7.x and you can see:
Eloquent will also assume that each table has a primary key column named id. You may define a protected $primaryKey property to override this convention:
That means that provided both your model tables have the primary key columns named "ID" ( as you showed you do in the chat ) your code should look like this:
class Manufacturer extends Model {
public function subcategories() {
return $this->belongsToMany(
'App\Subcategory',
'membercategory',
'FKManufacturerID',
'FKSubCategoryID'
'ID',
'ID'
);
}
}

Just replace the positions of third and fourth columns like this:
return $this->belongsToMany('App\Subcategory', 'membercategory', 'FKManufacturerID', 'FKSubCategoryID');

Related

How can I access model Relation with where condition of child and parent table in laravel?

I have Games table and which has the following schema
id | status | name
status column has 2 values (Active, Pending)
And GamePlayer table which has the following schema
id | game_id | player_id | request_status
request_status column has 3 values (Pending, Confirm, Rejected)
Now I have to select all game in which the player is involved but with the following constraints:
If the game is Pending state then it will be shown to all game_players
If the game is in Active state then it will be only shown to the game_player whose request_status is Confirm.
Game(Model)
public function GamePlayer()
{
return $this->hasMany('App\Models\GamePlayer', 'game_id', 'id');
}
public function getGames($playerId)
{
$gameList = Game::with(['GamePlayer','Category:id,name'])
->whereHas('GamePlayer', function ($q) use ($playerId) {
$q->where('player_id', $playerId);
})->get();
return $gameList;
}
Controller
$this->gameObj = new Game();
$gameList = $this->gameObj->getGames($player_id);
Please help me out how can I populate data from another table based on condition(parent table as well as the child).
You can use condition in your relationship to get only confirmed game players
public function activeGamePlayers()
{
return $this->hasMany('App\Models\GamePlayer', 'game_id', 'id')
->where('request_status', 'confirm');
}
You can as well use scopes (https://laravel.com/docs/7.x/eloquent#local-scopes) to select only active games
public function scopeActive($query)
{
return $query->where('status', 'active');
// You can then use $game->active()->...
}
So your getGame would look like:
public function getGames($playerId)
{
$player = GamePlayer::find($playerId);
if ($player->status == 'active') {
$gameList = Game::with(['GamePlayer', 'Category:id,name'])
->get();
} else {
$gameList = Game::with(['GamePlayer', 'Category:id,name'])
->active()->get();
}
return $gameList;
}
NOTE:
in your specific case, I would instead get started from the GamePlayer Model to get the games, instead of coming from the Game Model - you can use scopes and conditions in relationships as well to make your code more readable.
You can use Laravel method whereHas. You should read this part of Laravel the documentation.
Note that you should have relationships declared on every model.

How to query a table based on criteria of another table in Laravel?

In my laravel project I have an inventory and a medicine table which the formats are as the following:
Inventory Table
id | medicine_id | expiry_date
-------|-----------------|-------------
1 | 1 | 13-11-2021
2 | 2 | 01-01-2020
3 | 2 | 23-05-2024
Medicine Table
id | name | category
-------|-----------------|-------------
1 | Hemophine | Syringe
2 | andrenalin | Tablet
3 | aspirin | Capsule
The models are set as below:
Inventory Model
class Inventory extends Model
{
public $incrementing = false;
public function medicine(){
return $this->belongsTo('App\Medicine', 'medicine_id');
}
}
Medicine Model
class Medicine extends Model
{
public $incrementing = false;
public function inventory(){
return $this->hasMany('App\Inventory', 'medicine_id');
}
}
For retrieving all the inventories with a specific category such as Syringe, I tried eager loading, but couldn't figure out the flaws. Here is what I did to get all the inventories with a Syringe category, but it didn't work.
public function index()
{
$medicines = Inventory::where('medicine_id'->category, "Syringe")->get();
foreach($medicines as $medicine){
echo $medicine->medicine->name ." ".$medicine->expiry_date;
echo "<br>";
}
}
Now, Is there anyway to get all the inventory items based on their categories they are in? in this case in "Syringe" category?
Your syntax is a bit wrong, in order to load the medicine for each inventory you need to use with and the where function is where('column', value)
So change it to this:
$medicines = Inventory::with([
'medicine' => function($query) {
$query->where('category', "Syringe");
}
])->get();
or even better the other way around:
$medicines = Medicine::with('inventory')->where('category', 'Syringe')->get();

How to combine models and order by model two data in Laravel 5.4

I would like to be able to output leaders in a section by their position in a group. I'm thinking of combining these tables through a pivot table ordered by position from the groups table.
These are the three class I have relationships established
class Leader extends Model
{
public function groups()
{
return $this->belongsToMany('App\Group');
}
}
class Group extends Model
{
public function leaders()
{
return $this->belongsToMany('App\Leader');
}
}
class GroupLeader extends Model
{
// Maybe some function here?
}
I would like to be able to get the groups in the controller and pass them individually kinda like this.
$group1 = Leader::where('group', '=', 'group1_name')->orderBy('position', 'asc');
$group2 = Leader::where('group', '=', 'group2_name')->orderBy('position', 'asc');
return view('page.leadership')->with([
'group1' => $group1,
'group2' => $group2,
]);
Then I would pass the variables to include files which would loop through them in order.
Here is my table structure
leaders
--------
| id
| first_name
| last_name
| deceased
----------
groups
----------
| id
| group
| title
| position
-----------
group_leader
-------------
| id
| group_id
| leader_id
------------
Try it
$groups = Leader::whereIn('group', ['group1_name', 'group2_name'])
->orderBy('position', 'asc')
->groupBy('group', 'id')
->get(['group', 'id']);

Laravel - Eloquent - Polymorphic relationships with namespaces

I have 2 tables related by a polimorphic relationship:
The Store table:
id | name | ...
15 | my_store | ...
And the Tag table, that is connected to the Store table through the related_id key and the type.
id | related_id | tag | created | type
1 | 15 | test | 00:00:00 | store
2 | 15 | dummy | 00:00:00 | product
So, in that example, the Store "my_store" only has the tag "test" ("dummy" is a tag for a product, not a Store, besides it has the same related_id).
I have the Store model defined in my code as follows:
class Store extends Model {
protected $table = 'store';
public function tags()
{
return $this->morphMany('App\Tag', 'related', 'type');
}
}
And the tag model:
class Tag extends Model {
protected $table = 'tags';
public function related()
{
return $this->morphTo();
}
}
But when I try to run
$store->tags;
I saw that the query that Laravel is trying to run is:
SQL: select * from `mwp_tags` where `mwp_tags`.`related_id` = 46
and `mwp_tags`.`related_id` is not null
and `mwp_tags`.`type` = App\Store)
The query is looking for App\Store instead of store
I cannot change the value in DB and I wouldn't like to use the
protected $morphClass = 'store';
inside the Store model as I don't know if a would have to create another morphMany relationship, probably with another type name.
Somebody knows how to skip this issue? Thanks!
You could try something like this:
public function tags()
{
$default_morph_class = $this->morphClass ?: null;
$this->morphClass = 'store';
$morph_many_query = $this->morphMany('App\Tag', 'related', 'type');
$this->morphClass = $default_morph_class;
return $morph_many_query;
}
The idea being you change the morphClass on the fly and set it back. That allows you to use a different morphClass for a different relationship.

laravel 4 model events updating a record on softdelete

[Help] I need when deleting a record it will also create a deleted_by value.
[company table]
| name | created_by | updated_by | deleted_by | created_at | updated_at | deleted_at |
public static function boot()
{
static::creating(function($company)
{
$company->created_by = Auth::user()->id;
});
}
This will fill created by with the user id when we creating record.
But when I use this when delete record ( using soft delete )
public static function boot()
{
parent::boot();
static::deleting(function($company)
{
$company->deleted_by = Auth::user()->id;
});
}
this method is doesn't work, deleted_by column is not update.
How to fill deleted_by with user id when we delete a record ?
Thanks before
At the moment, Laravel's delete() method does not take other attribute changes into consideration when it creates the relevant UPDATE query. Here's a snippet from Eloquent\Model::performDeleteOnModel():
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
if ($this->softDelete)
{
$this->{static::DELETED_AT} = $time = $this->freshTimestamp();
$query->update(array(static::DELETED_AT => $this->fromDateTime($time)));
}
else
{
$query->delete();
}
Because of the newQuery(), no changes made to the object are taken into consideration when building the actual DB query.
You could extend the Model class and add in your own functionality to accept any changed attributes, or you can simply toss $company->save() in your static::deleting() closure. It will perform two queries, but will take you mere seconds to implement, compared to the first option. Your choice!

Resources