Use a different column on a many-to-many relationship in Laravel 4 - laravel

I've got a situation in a project where I need to form a relationship between a primary key on one table and an indexed column (not the primary key) on another. Here's a sample of the database layout:
courses table
id
level
resources table
id
courses_resources table
course_level
resource_id
In my CourseResource model I have the following:
public function courses(){
return $this->belongsToMany('Course', 'courses_resources', 'resource_id', 'course_level');
}
Which works fine.
Then in my Course model I have:
public function resources(){
return $this->belongsToMany('CourseResource', 'course_resources', 'course_level', 'resource_id');
}
Which doesn't work. When I look at the last query performed on the database, it appears Laravel is searching the course_level column using the course's ID. That makes sense, but is there any way to use the level column for this relationship?

Eloquent BelongsToMany depends on PKs, so there is no way to do that with its methods.
You need custom relation object for this, that will check for given field, instead of primary key.
A quick and hacky solution would be this:
// Course model
public function getKey()
{
$relation = array_get(debug_backtrace(1, 2), '1.object', null);
if (method_exists($relation, 'getForeignKey')
&& $relation->getForeignKey() == 'courses_resources.course_level')
{
return $this->getAttribute('level');
}
return parent::getKey();
}
However if you would like to use it in production, do some extensive testing first.

Related

Get data through pivot table in Laravel

I got 3 tables. Table 1 & 2 has their ids as foreign keys in third one(pivot).
Relations for first one is
$this->hasMany("App\Pivot","game_id");
, second is
$this->belongsToMany("App\Pivot","army_id");
and pivot has relationships with both of them i.e belongsTo.
My schema:
I tried accessing it in controller of first one like this:
$games= Game::with("armies")->get();
Result that i get is array of games where instead of individual army data , i get collection from pivot table.
I can loop through it and get it that way, is there more elegant way of doing it?
If you are using pivot table this is the way how to do it.
Games Model
public function armies()
{
return $this->belongsToMany(App\Armies::class, 'pivot_table', 'game_id', 'army_id');
}
Armies Model
public function armies()
{
return $this->belongsToMany(App\Games::class, 'pivot_table', 'army_id', 'game_id');
}
Access the relationship like this..
Controller
App\Games::first()->armies()->get();
or
App\Games::first()->armies
or
App\Games::find(1)->armies
If you're going to use an intermediate table like that I'd probably do something like this:
Games model
public function armies()
{
return $this->belongsToMany('App\Armies');
}
Armies model
public function games()
{
return $this->belongsToMany('App\Games');
}
I'd keep the table structures all the same but rename the "pivot" table to armies_games since this is what Laravel will look for by default. If you want to keep it named Pivots, you'll need to pass it in as the second argument in belongsToMany.
With this, you don't really need the Pivot model, you should just be able to do:
$armies = Game::first()->armies()->get();
or
$armies = Game::find(3)->armies()->orderBy('name')->get();
or
$game = Game::first();
foreach ($game->armies as $army) {
//
}
etc.

Eloquent custom relationship hasMany (foreign field contains text concatenated by foreign key)

I have this database structure. 2 tables: shipment_out, stock_move.
shipment_out has the typical primary key integer id field.
stock_move has a field named shipment which is string type. This field can have these values:
"stock_shipment_out,1512",
"stock_shipment_in,65400",
"sale.line,358",
(...)
The thing is the table stock_move is related to a multiple tables based on the same field, so it has this text before.
In this case I want to define the relationship: shipment_out hasMany stock_move.
So I need to join by stock_move.shipment has this value: 'stock_shipment_out,{id}'.
So how can I define this relationship? Would be something like:
public function stockMoves()
{
return $this->hasMany(StockMove::class, 'shipment', 'stock.shipment.out,id');
}
I can achieve this relationship with query builder:
$shipments = ShipmentOut
::join('public.stock_move', DB::raw('CONCAT(\'stock.shipment.out,\',public.stock_shipment_out.id)'), '=', 'stock_move.shipment')
->where('stock_shipment_out.id', '=', $shipmentOut);
But I need on a relationship too...
To solve this problem I had to define a custom attribute, and then I can define the relationship with this field.
public function getStockMoveShipmentAttribute()
{
return "stock.shipment.out,{$this->id}";
}
public function stockMoves()
{
return $this->hasMany(StockMove::class, 'shipment', 'stock_move_shipment')
}
Now I can use this relationship, but it's only one-direction...
If I want to define the same relationship as the inverse it doesn't work.
I opened another question explaining it: Laravel relationship based on custom attribute not working both directions

How to retrieve data through model?

I have Order model with another relation OrderPhoto:
public function OrderPhoto()
{
return $this->hasMany('App\OrderPhoto');
}
In turn OrderPhoto model has relation:
public function Photo()
{
return $this->belongsToMany('App\Photo');
}
So, how to get data from OrderModel with related data from third model Photo?
I guess this:
Order::with("OrderPhoto.Photo")->get();
to retrieve only data from Photo model for each Order
So, each Order has some OrderPhotos. Relationship is one to many.
But one item from OrderPhotos is related with primary key from table Photos. It is one to one relation.
My result query should be:
select `photos`.*, `ordersphoto`.`Orders_Id` from `photos` inner join `ordersphoto` on `ordersphoto`.`Photos_Id` = `photos`.`Id` where `ordersphoto`.`Orders_Id` in (1);
How to use hasManyThrough for this query?
Just having a quick look at your relationships it looks like you could create a hasManyThrough relationship on the order Model.
public function Photo {
return $this->hasManyThrough('App\OrderPhoto', 'App\Photo')
}
You may need to add the table keys to make it work
This will allow you to do:
Order::with("Photo")->get();
You can see more details here https://laravel.com/docs/5.5/eloquent-relationships#has-many-through
Update
Try this
public function Photo {
return $this->hasManyThrough('App\Photo', 'App\OrderPhoto', 'Order_id', 'Photos_id', 'id', 'id')
}
It is a little hard to get my head around your DB structure with this info but you should hopefully be able to work it out. This may also help
https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_hasManyThrough

Selecting specific column in ORM relationship Laravel 5.5

I have an order table with a primary key id and another table trips. I trip can have many orders. I have defined the relationship in this way
public function orders_currency() {
return $this->hasMany('App\Order', 'trip_id', 'id')->select('id', 'converted_currency_code');
}
Although I know that, I have to select id of the order table in order to get the result, that's why I am mentioning to select the id of the order table.
Despite this, I am getting the orders_currency index empty. Which angle am I missing in this case?
Any clue will be appreciated.
Did you set up the model correctly?
You'd do something like this in your model script I suppose
class Trips extends Model
{
public function orders() {
return $this->hasMany('Order');
}
I got the solution by doing this way
public function orders_currency() {
return $this->hasOne('App\Order', 'trip_id', 'id')
->select('trip_id','converted_currency_code');
}
I had to select referencing key in this case.

Laravel - Using different field names in the database (userid in a table and created_by in other)

I am trying to use a table for my Users and separate table for users' Projects in my database. However I want the names of the fields to be different for user id. What I want to take the id from the 'Users' table; and while saving the created project to the database, use that (user) id as created_by_id in Projects table.
public function store(CreateProjectRequest $request)
{
$project = new Project($request->all());
Auth::user()->projects()->save($project);
// Project::create($request->all());
return redirect('pages/home');
}
Also in Users.php, I added:
public function projects()
{
return $this->hasMany('App\Project');
}
The commented field is working on its own. However, I guess my problem arises because when I comment that line again, and add the other two lines ($project... and Auth::user... bits), I guess it is assuming I have a field in the Projects table named id.
I thought I would work around this problem with changing the primary key but I couldn't find how to take the Auth::user()->id; and make it write that value in created_by_id in a secure way. This is what I found though:
class Project extends Eloquent {
protected $primaryKey = 'created_by_id';
}
Edit: I don't think changing the primary key is my solution.
You can pass a second and third parameter to hasMany() method to specify the keys to use. Documentation
public function projects()
{
return $this->hasMany('App\Article','userid','created_by');
}

Resources