Laravel 5 Pivot Table Extra Field - laravel-5

What I’m trying to do is when a new candidate is created an extra field is automatically populated in the joining pivot table with a random sting.
Here is my pivot table:
Result Table (pivot)
id cert_number candidate_id qualification_id
1 ? 17 2
2 ? 17 1
3 ? 57 1
So in my candidate controller I have:
public function store(CandidateRequest $request)
{
$candidateInput = Input::get('candidates');
foreach ($candidateInput as $candidate)
{
$candidate = Candidate::create($candidate);
$candidate->centre()->attach(Auth::user()->centre);
$qualification_id = $request->input('qualification_id');
$candidate->qualification()->attach($qualification_id);
$cert_number = Str::random(10);
$candidate->qualification()->attach($cert_number);
}
return redirect('candidates');
}
It adds the centre_id and qualification_id perfectly but it won’t pull though the random sting into the cert_nubmer field.
In my Candidate model I have
public function result()
{
return $this->hasMany('App\Result')->withTimestamps();
}
public function qualification()
{
return $this->belongsToMany('App\Qualification', 'Results', 'candidate_id', 'qualification_id')->withPivot('status','cert_number','partial_claim')->withTimestamps();
}
and in my result model:
public function candidate()
{
return $this->belongsTo('App\Candidate')->withTimestamps();
}
public function qualification()
{
return $this->belongsTo('App\Qualification');
}
Can anyone see where Im going wrong?
Thanks.

attach does not work like that.
Let me take out two lines of code:
$candidate->qualification()->attach($qualification_id);
$candidate->qualification()->attach($cert_number);
You are trying to add the $cert_number as a qualification_id, which is why it fails. When you do this, how is Laravel supposed to know that the second line ($cert_number) is an extra pivot column? It doesn't. You have two lines of code that are exactly the same so you can't expect Laravel to know that the second line should do something different.
When you want to insert extra data into other pivot columns, you need to pass them as an array in the second argument. Something like this:
$candidate->qualification()->attach($qualification_id, ['cert_number' => $cert_number]);

Related

Laravel get documents that has x value in append attribute

Case Scenario Model User that has append attribute called custom
this custom attribute might differ between one document to the other
for example
public function getCustomAttribute()
{
return $this->id % 2;//this is an example to showcase that it will be different but it's not my case
}
this will return 0 or 1 depending on if the id is even or odd
I want to get only users with the 1 value how can I achieve this in the query since I want to use pagination
As I understand your questions correct you can try to do like that:
protected $appends = ['custom'];
public function getCustomAttribute(): int
{
return (int) $this->attributes['id'] % 2;
}

Count multiple columns in Laravel query builder

I'm studying laravel query builder's "count"
I would like to count column name 'q12a' and 'q18a'
I can count total records useing below code.
public function count()
{
$total_projects = Book::count();
return view('count')->with(['total'=>$total_projects]);
}
However I'm having problem multiple columns
I had been searching count multiple columns and trying below code and I got
Error Call to a member function count() on int
Dear matiaslauriti helping me and I change code as below.
UPDATED
public function sum_ttl()
{
$q18a_count = DB::table('books')->count('q12a')->count('q18a');
return view('sum_ttl', compact('q12a','q18a'));
}
Could you teach me how to write correct code at controller and blade file please?
You are nearly there. count will return the count of your desired columns (* by default). So you want to do something like:
public function sum_ttl()
{
$q12aCount = Books::count('q12a');
$q18aCount = Books::count('q18a');
return view('sum_ttl', compact('q12aCount', 'q18aCount'));
}
If you want to share the exact SQL query you want to execute, I could try to "translate" it to Eloquent.

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.

How to GROUP and SUM a pivot table column in Eloquent relationship?

In Laravel 4; I have model Project and Part, they have a many-to-many relationship with a pivot table project_part. The pivot table has a column count which contains the number of a part ID used on a project, e.g.:
id project_id part_id count
24 6 230 3
Here the project_id 6, is using 3 pieces of part_id 230.
One part may be listed multiple times for the same project, e.g.:
id project_id part_id count
24 6 230 3
92 6 230 1
When I show a parts list for my project I do not want to show part_id twice, so i group the results.
My Projects model has this:
public function parts()
{
return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
->withPivot('count')
->withTimestamps()
->groupBy('pivot_part_id')
}
But of course my count value is not correct, and here comes my problem: How do I get the sum of all grouped parts for a project?
Meaning that my parts list for project_id 6 should look like:
part_id count
230 4
I would really like to have it in the Projects-Parts relationship so I can eager load it.
I can not wrap my head around how to do this without getting the N+1 problem, any insight is appreciated.
Update: As a temporary work-around I have created a presenter method to get the total part count in a project. But this is giving me the N+1 issue.
public function sumPart($project_id)
{
$parts = DB::table('project_part')
->where('project_id', $project_id)
->where('part_id', $this->id)
->sum('count');
return $parts;
}
Try to sum in Collection,
$project->parts->sum('pivot.count');
This is best way I found. It's clean (easy to read) and able to re-use all of your scope, ordering and relation attribute caching in parts many-to-many defination.
#hebron No N+1 problem for this solution if you use with('parts') to eager load. Because $project->parts (without funtion call) is a cached attribute, return a instance of Collection with all your data. And sum('pivot.count') is a method of Collection which contains pure funcional helpers (not relative to database, like underscore in js world).
Full example:
Definition of relation parts:
class Project extends Model
{
public function parts()
{
return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
->withPivot('count')
->withTimestamps();
}
}
When you use it (note that eager load is important to avoid N+1 problem),
App\Project::with('parts')->get()->each(function ($project) {
dump($project->parts->sum('pivot.count'));
});
Or you can define the sum function in Project.php,
class Project extends Model
{
...
/**
* Get parts count.
*
* #return integer
*/
public function partsCount()
{
return $this->parts->sum('pivot.count');
}
}
If you want to avoid with('parts') on caller side (eager load parts by default), you can add a $with attribute
class Project extends Model
{
/**
* The relations to eager load on every query.
*
* #var array
*/
protected $with = ['parts'];
...
}
From the code source:
We need to alias all of the pivot columns with the "pivot_" prefix so we can easily extract them out of the models and put them into the pivot relationships when they are retrieved and hydrated into the models.
So you can do the same with select method
public function parts()
{
return $this->belongsToMany('Part', 'project_part', 'project_id', 'part_id')
->selectRaw('parts.*, sum(project_part.count) as pivot_count')
->withTimestamps()
->groupBy('project_part.pivot_part_id')
}
The best way that you can use is:
$project->parts->sum('pivot.count');
I faced the same problem, but this solved my issue.

Laravel Has One Relation changing the identifier value

I'm not sure this is a real relation. I will try to explain the best way I can.
So first of all, I have three models :
Appartement,
AppartementPrice
The AppartementPrice depends on :
- appartement_id
I would like the AppartementPrice to be retrieve like that :
If there is a specific price for the appartement, then retrieve it, If not retrieve the price for all appartement which is stored in the database with an appartement_id = 0.
So basically what I would like is to do something like that :
public function price()
{
if(isset($this->hasOne('AppartementPrice')->price) // Check that relation exists
return $this->hasOne('AppartementPrice');
else
return $this->hasOne('AppartementPrice')->where('appartement_id', '0');
}
But this is not working.
It does not retrive me the default price.
I guess anyway this is not a best practice ?
I first tried to get the informations like that :
//Check if appartment has a specific price or retrieve default
if($priceAppartement = AppartementPrice::getPriceByCompanyAppartement($this->id))
return $priceAppartement;
else
return AppartementPrice::getDefaultPrice();
But I had this error :
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
when doing :
echo $app->price->price;
How can I check that a relation exists ? And is there a way to do as I describe ?
Thank you
You can't replace relation like this, as what you intend is not logical - you want to retrieve relation that doesn't exist.
Instead you can do this:
public function getPriceAttribute()
{
return ($this->priceRelation) ?: $this->priceDefault();
}
public function priceDefault()
{
// edit: let's cache this one so you don't call the query everytime
// you want the price
return AppartmentPrice::remember(5)->find(0);
}
public function priceRelation()
{
return $this->hasOne('AppartementPrice');
}
Then you achieve what you wanted:
$app->price; // returns AppartmentPrice object related or default one
HOWEVER mind that you won't be able to work on the relation like normally:
$price = new AppartmentPrice([...]);
$app->price()->save($price); // will not work, instead use:
$app->priceRelation()->save($price);
First of all something really important in Laravel 4.
When you do not use parentheses when querying relationship it means you want to retreive a Collention of your Model.
You have to use parentheses if you want to continue your query.
Ex:
// for getting prices collection (if not hasOne). (look like AppartementPrice)
$appartment->price;
// for getting the query which will ask the DB to get all
//price attached to this appartment, and then you can continue querying
$priceQuery = $appartment->price();
// Or you can chain your query
$appartment->price()->where('price', '>', 0)->get() // or first() or count();
Secondly, your question.
//Appartement Model
// This function is needed to keep querying the DB
public function price()
{
return $this->hasOne('AppartementPrice')
}
// This one is for getting the appartment price, like you want to
public function getAppartmentPrice()
{
$price_object = $this->price;
if (!$price_object) // Appartment does not have any price {
return AppartementPrice->where('appartement_id', '=', 0)->get();
}
return $price_object;
}

Resources