I have 2 tables
cars (id, title)
parts (id, title)
and I want to assosiate every car with a lot of parts and the price of this part for the specific car...
I believe the best way is to associate them through a table:
car_parts(id, car_id, part_id, price)
How do I define such a relation in Laravel's Eloquent?
I want to do
$car = Car::find(1);
$parts = $car->parts;
and I want to get an array of objects like so
{
'id' => 1,
'title' => 'rear flash',
'price' => '10.00',
},{
...
}
if I try the
public function parts(){
return $this->belongsToMany('Part', 'car_parts', 'car_id', 'part_id');
}
I dont get the price...
TIA
The price is available on the pivot model if you include it in withPivot:
public function parts ()
{
return $this->belongsToMany('Part', 'car_parts', 'car_id', 'part_id')
->withPivot('price');
}
Then you can map over the parts collection to get the arrays you want:
$parts = Car::find(1)->parts->map(function ($part)
{
return [
'id' => $part->id,
'title' => $part->title,
'car_id' => $part->pivot->car_id,
'part_id' => $part->pivot->part_id,
'price' => $part->pivot->price,
];
});
Related
How to use updateOrCreate with hasMany relationship. For example I have first model Code:
class Code {
public function item()
{
return $this->hasOne(UserItem::class, 'code_id')
}
}
And for the nested relationship UserItem:
class UserItem {
public function serials()
{
return $this->hasMany(ItemSerial::class, 'user_item_id', 'id');
}
}
And I need to updateOrCreate values of serials relationship. I tried this:
foreach ($data['item_serials'] as $serial) {
$code->item->serials()->updateOrCreate([
'serial' => $serial
]);
}
But this doesn't work how I need, because it changes both serial values to same value. This is how serials table looks like:
id user_item_id serial
1 1 lorem
2 1 ipsum
And then I recieve request:
'item_serials' =>
array (
0 => 'test1',
1 => 'test2',
2 => 'test3'
),
And with this request I want to update serials table like this:
id user_item_id serial
1 1 test1
2 1 test2
3 1 test3
I hope I explained understandably. How I should approach this?
Update or create takes two arguments
1-updated data
2-condition
like
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
see the docs https://laravel.com/docs/8.x/eloquent
Add an array as the first argument inside updateOrCreate(), it'll be used to retrieve/find an existing serial if it does update but if not then create it.
foreach ($data['item_serials'] as $serial) {
$code->item->serials()->updateOrCreate(
['serial' => $serial],
['serial' => $serial]
);
}
I have 3 models:
Match Team Player
And i want to create a table with the following structure:
id | match_id | team_id | player_id
So that i can associate the 3 models i refered.
I created a 4th model MatchPlayers for the table i referred and I can use the 'search' functions without a problem. Like this:
$match->matchPlayers()->first()->team()->get()
And it returns the excpected result, but I cant do a
$match->matchPlayers()->sync([])
So, how should i solve this? Is my relationship wrong or the sync method isnt allowed on a 3 model relationship and I shoud use other method?
Thanks in advance
Edit:
Match.php
public function teamPlayers(){
return $this->hasMany('\Modules\Matchs\Entities\MatchPlayer');
}
Team.php
public function matchTeamPlayers(){
return $this->hasMany('\Modules\Matchs\Entities\MatchPlayer');
}
Player.php
public function matchTeamPlayers(){
return $this->hasMany('\Modules\Matchs\Entities\MatchPlayer');
}
MatchPlayer.php
public function player(){
return $this->belongsTo('\Modules\Players\Entities\Player');
}
public function match(){
return $this->belongsTo('\Modules\Matchs\Entities\Match');
}
public function team(){
return $this->belongsTo('\Modules\Teams\Entities\Team');
}
If you've followed the Laravel documentation on Pivot tables and Many-Many relationships found here, and it's still not working, you might have more luck with "Attach". For example;
$matchPlayer = MatchPlayer::create([...]);
$match->matchPlayers()->attach($matchPlayer)
A good example of sync vs attach can be found here
Using a fourth model for this kind of relationship makes sense, as it gives you a navigation property for the third relation on your pivot table. This way you can form more complex queries this way.
For your particular problem, syncing based on match_id and team_id, I would simply do something like this:
$matchId = 123;
$teamId = 234;
$rows = [
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 345],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 346],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 347],
];
// remove all previously stored connections
MatchPlayer::where('match_id', $matchId)
->where('team_id', $teamId)
->delete();
// insert the news ones
// (you could also use MatchPlayer::create() per item or
// $matchPlayer->save(), it doesn't matter)
MatchPlayer::insert($rows);
If this operation occurs very frequently, you will potentially burn through a lot of id values of the pivot table. In this case you could also perform a more efficient sync, which is slightly more complex:
$matchId = 123;
$teamId = 234;
$rows = [
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 345],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 346],
['match_id' => $matchId, 'team_id' => $teamId, 'player_id' => 347],
];
// delete all players that are not among the new data anymore
MatchPlayer::where('match_id', $matchId)
->where('team_id', $teamId)
->whereNotIn('player_id', array_pluck($rows, 'player_id'))
->delete();
// remove rows from new data that already exist
$exist = MatchPlayer::where('match_id', $matchId)
->where('team_id', $teamId)
->pluck('player_id')
->toArray();
$rows = array_filter($rows, function ($value, $key) use ($exist) {
return ! in_array($value['player_id'], $exist);
});
// then we store the remaining data
MatchPlayer::insert($rows);
I have 9 tables that will love to join together all have the foreign key employee_id from employee table. How can I get ORM distribution for it. Below is the DB function that join all the function.
$modelEmployee = \DB::table('employees')
->select('*')
->join('employee_finances', 'employees.id', '=', 'employee_finances.employee_id')
->join('employee_addresses', 'employees.id', '=', 'employee_addresses.employee_id')
->join('employee_jobs', 'employees.id', '=', 'employee_jobs.employee_id')
->join('employee_admins', 'employees.id', '=', 'employee_admins.employee_id')
->join('employee_personals', 'employees.id', '=', 'employee_personals.employee_id')
->join('employee_memberships', 'employees.id', '=', 'employee_memberships.employee_id')
->where('employees.id', $id)
->get();
Step 1
First, create model for you employee table and add various relations to other table models
Eloquent Model for employees table
namespace App\Models;
class Employee extends \Illuminate\Database\Eloquent\Model {
public function employee_finances()
{
return $this->hasMany(\App\Models\EmployeeFinance);
}
public function employee_addresses()
{
return $this->hasMany(\App\Models\EmployeeAddress);
}
public function employee_jobs()
{
return $this->hasMany(\App\Models\EmployeeJob);
}
public function employee_admins()
{
return $this->hasMany(\App\Models\EmployeeAdmin);
}
public function employee_personals()
{
return $this->hasMany(\App\Models\EmployeePersonal);
}
public function employee_memberships()
{
return $this->hasMany(\App\Models\EmployeeMembership);
}
}
Step 2
Now create models for other join tables. Below is an example of employee_finances table. (similarly, create other models)
namespace App\Models;
class EmployeeFinance extends \Illuminate\Database\Eloquent\Model {
...
}
Step 3
Then for your query, you can use relations using with and whereHas functions of query builder. This equivalent to the result of the query mentioned in the question but the structure of the outcome will be different;
\App\Models\Employee::with('employee_finances','employee_addresses','employee_jobs','employee_admins','employee_personals','employee_memberships')
->whereId($employeeid)
->whereHas('employee_finances')
->whereHas('employee_addresses')
->whereHas('employee_jobs')
->whereHas('employee_admins')
->whereHas('employee_personals')
->whereHas('employee_memberships')
->get();
Result
Original Result object
The original resultant will be an object of common builder object where you cannot fire further relation actions which can be defined in Model level.
The original result will also be a flat array of the result and may have less. One example here would be the id column value would be replaced by the primary employee's table column id.
[
0 => [
'id' => 1,
'employee_name' => 'Employee',
'employee_finance_content' => 'finance_content',
'employee_personal_content' => 'personal_content',
'employee_jobs_content' => 'employee_jobs',
'employee_addresses_content' => 'employee_addresses',
'employee_admins_content' => 'employee_admins',
]
....
]
New result using Models
The result would be an instance of Employee model. The final result would be as an associative array where each relation would be an index of the array but the result will be an instance of the related Model, for example, the employee_finances would be an index or represent a column and the value contained within it will be an instance of EmployeeFinance on which you can further do ORM level activities.
[
0 => [
'id' => 1,
'employee_name' => 'Employee'
'employee_finances' => [
'id' => 2,
'employee_id' => 1,
'employee_finance_content' => 'finance_content'
],
'employee_addresses' => [
'id' => 10,
'employee_id' => 1,
'employee_address_content' => 'employee_address'
]
],
.....
]
you can use from this :
$row = $this->model->
where("id",$id)
->with("employee_finances")
->with("employee_addresses")
->with("employee_jobs")
->with("employee_admins")
->with("employee_personals")
->with("employee_memberships")
->with("employee_finances")
->get();
return $row->isEmpty() ? [] : $row->toArray();
plz define relations in your models with these names and use form that, here.
I have three Mysql tables:
Brands ( brand names , brand Ids)
cars ( car names,car ids, brand Ids)
statuss ( car Ids, each car status).
I am trying to pull the brand names from the brand table but I have the cars IDs. How can I do that see my code below.
Controller.ctp
$this->loadModel('car');
$cars = $this->car>find('all',array('limit' => 6, 'order' => array('car.id' => 'asc')));
$this->set('cars', $cars);
$this->loadModel('status');
$cars = $this->status>find('all',array('limit' => 6, 'order' => array('status.id' => 'asc')));
$this->set('statuss', $statuss);
View.ctp
App::import('Controller', 'cars');
$carsCont = new carsController;
App::import('Controller', 'brands');
$brandCont = new brandsController;
foreach($statuss as $status)
{
$car_info = $carsCont->get_car_info($status['status']['car_id']);
$car_name = $car_info['car_name'];
$car_id = $status['status']['car_id'];
$brand_list = $brandCont -> get_brand_name($status['car']['brand_id']); <---- this is not working
echo $brand_list['brand']['brand']; echo $car_name;
}
CakePHP controllers are not designed or meant to be instantiated directly like you are doing.
Rather, you should be making use of the associations that cakephp gives you.
Based on what you've said, you've got Statuses belongsTo Cars belongsTo Brands. Set those relations up in your model files (as documented in the book: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html)
If you then use the containable behavior (http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html) you'd be able to do something like (In your status controller)
$statuses = $this->Statuses->find('all', [
'contain' => ['Cars' => 'Brands']
]);
and you should have all the data that you need.
Edit: Based on your additions, change your cars query to
$cars = $this->car>find('all',array('limit' => 6, 'order' => array('car.id' => 'asc'), 'contain' => ['Brands']));
But, make sure Cars BelongsTo Brands, and you've attached the Containable behaviour.
My application (Laravel 5.0) has a Products table and a Formats table. There's a manyToMany relationship between these two table (format_product). One product can be sold in many formats. Each relationship has a specific price so I have added a "price" column in the format_product table.
Now I'm trying to sort the products by price (being the cheapest format-price of each product the reference value).
One more thing, I need to paginate the results.
class Product extends Model {
public function formats()
{
return $this->belongsToMany('App\Format')->withPivot('price')->orderBy('pivot_price', 'asc');
}
}
class Format extends Model {
public function products()
{
return $this->belongsToMany('App\Product')->withPivot('price');
}
}
This is the format_product_pivot:
Schema::create('format_product', function(Blueprint $table) {
$table->integer('format_id')->unsigned()->index();
$table->foreign('format_id')->references('id')->on('formats')->onDelete('cascade');
$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->decimal('price', 8, 2);
});
In example, having this values:
Product A - Format 1 = 15€
Product A - Format 2 = 10€
Product B - Format 1 = 8€
Product B - Format 2 = 20€
Product C - Format 3 = 5€
Product C - Format 1 = 2€
I want this result:
Product C - 1 ( 2€)
Product B - 1 ( 8€)
Product A - 2 (10€)
Ok so I don't typically put orderBy() in my model, but it shouldn't be too much of an issue. You're going to have to use a join to get the results you want.
You can use the following query in your controller:
public function index() {
$products = Product::join('format_product', 'format_product.product_id', '=', 'products.id')
->select('products.id', 'format_product.format_id', 'format_product.price')
->orderBy('format_product.price', 'asc')
->paginate(25)
->get();
}
The reason you can't sort products by a relationship is the same reason you can't sort a multidimensional array by an inner array.
For example:
$array = [
'key1' => [
'anotherKey' => 'some value'
],
'key2' => [
'anotherKey' => 'some other value',
'anotherKey2' => 'some other value2'
],
'key3' => [
'anotherKey' => 'yet another value'
]
]
You can't sort this array by anotherKey. You have to use a join.
Hope this helps.