I have a Quote model:
class Quote extends Eloquent {
public function quote_lines() {
return $this->hasMany('QuoteLine');
}
}
And a QuoteLine model:
class QuoteLine extends Eloquent {
public function quote() {
return $this->belongsTo('Quote');
}
}
My table for this model is quote_lines.
Schema:
Schema::create('quote_lines', function($table)
{
$table->increments('id');
$table->integer('quote_id');
$table->string('title');
$table->string('desciption');
$table->integer('quantity');
$table->decimal('unit_price', 5, 2);
$table->timestamps();
});
Schema::create('quotes', function($table)
{
$table->increments('id');
$table->integer('contact_id');
$table->integer('company_id');
$table->timestamps();
});
The thing is I can't seem to access the quote lines from my Quote controller:
$quote_lines = Quote::find($id)->quote_lines;
dd($quote_lines);
Just returns NULL
The method name for a relationship MUST be in proper camel-case in order to use the magic property. E.g., In order to use, Quote::find($id)->quote_lines, the method must be named quoteLines()
Allow me to explain:
Calling Quote::find($id)->quote_lines; activates the magic __get method in the Eloquent class. __get() calls getAttribute($key) on the property being retrieved. So in this case, it calls getAttribute('quote_lines')
If the key (quote_lines) is not available in the model's attributes array, it will instead search for a method in the class. It does this by transforming the key via camel_case(). Laravel's camel_case() method will transform quote_lines into quoteLines. It then looks for a function in your class named quoteLines(), and treats it as a relationship method if its found.
Therefore, the relationship method MUST be written as quoteLines. (or any string that is proper camel case)
public function quoteLines() {
return $this->hasMany('QuoteLine');
}
You may then access this relationship by using either Quote::find($id)->quote_lines or Quote::find($id)->quoteLines (both will work as both 'quote_lines' and 'quoteLines' evaluate to 'quoteLines' when camel-cased.)
Footnote: "camel case" in laravel refers to a string of this form: heyThereWorld.
The first letter must be lowercase, and the first letter of all subsequent words must be capitalized
I have the exact same problem with Laravel 4.1, and the solution suggested by this comment solved it for me.
Putting an underscore anywhere in the function name causes it to return null:
public function quo_telines() {
return $this->hasMany('QuoteLine');
}
Removing the underscore resolves the issue. Apparently, Eloquent doesn't like to have an underscore in the function name.
However, note that the function name must not match any of the schema's column names, or you'll get other issues.
You do not call the function, but you want to grab variable named $quote_lines.
Try to call $quote_lines = Quote::find($id)->quote_lines(); instead.
like #Virtlink and #TonyArra said this error is fixed if you use camelCase name in relation functions (this solution worked for me). For example I had
public function origin_city()
{
return $this->belongsTo('City', 'origin_city_id');
}
This was not working, then I changed it to
public function origincity()
{
return $this->belongsTo('City', 'origin_city_id');
}
and it didn't work. Only when I used camel case conventions did it work
public function originCity()
{
return $this->belongsTo('City', 'origin_city_id');
}
Hope it helps others
At the very least, it should be returning an empty eloquent object.
In your QuoteLine model, make sure you are telling Eloquent your table name.
public $table = 'quote_lines';
In setting up your relationship on your Quote model, be sure it knows what your foreign key is to link to your quote_lines table.
return $this->hasMany('QuoteLine','quote_line_id');
Related
So I was wandering in the source code of the HasRelationsShips trait and found that there’s this $relation parameter in methods like belongsTo that doesn’t show up in Laravel’s doc examples and by the looks of it serves only to debug purposes. Is this correct?
It is not for debug purposes, it is so you can define the relation name explicitly. Without that argument the BelongsTo will implicitly use the calling method name as the 'relation' name, example:
function user() {
return $this->belongsTo(User::class);
}
This will use a backtrace to know that the belongsTo was called from a method named user. So it will know the relation is user. Since we didn't explicitly tell it any foreign key it will use the relation, user, to build the foreign key: $relation .'_'. $relatedInstance->getKeyName() which would build user_id. This relation name is also used so that other relationship functionality knows what the name of the relationship method is on this model for this relationship, such as the associate and disassociate methods.
Just to elaborate on lagbox's answer, the use of a backtrace happens in Illuminate\Database\Eloquent\Concerns\HasRelations::guessBelongsToRelation() and looks like this:
protected function guessBelongsToRelation()
{
[$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
return $caller['function'];
}
The use of debug_backtrace() is controversial – and I'm not a big fan of micro-optimizations generally – but in this case with modern PHP it's very easy to skip the function altogether.
All my belongs-to relationships are simply defined like this, using a named argument and magic constant:
public function user(): BelongsTo
{
return $this->belongsTo(User::class, relation: __FUNCTION__);
}
First of all, this is my first Laravel project, it's just something I'm knocking together to learn, so please go easy on me.
Let's say I'm creating an application representing a library. Each Book has some info (BookInfo) about it, and each BookInfo contains a (reference to) a BookType that says if it's "fiction" or "non-fiction".
So if my BookInfo schema looks somewhat like:
//...
$table->unsignedBigInteger('book_id');
$table->unsignedBigInteger('book_type_id');
//...
$table->foreign('book_id')->references('id)->on('books');
$table->foreign('book_type_id')->references('id)->on('book_type');
And my BookType schema looks like this:
//...
$table->id();
$table->string('name');
What does the method on the BookInfo model look like that returns the BookType?
I've got this, and seem to have tried innumerable alternatives (with slightly different errors):
public function type()
{
return BookType::where('book_type_id', $this->book_type_id)->get()->first();
}
And yes, I'm sure that the id I'm trying to lookup there exists in the book_type table. I actually only really want the ->name of the BookType but obviously I need to get the BookType first...
public function type(): BelongsTo
{
return $this->belongsTo(BookType::class, 'book_type_id');
}
OR if you change the method name to bookType you can omit the second argument to belongsTo because it will automatically resolve the foreign key based on the method name, and you can just do:
public function bookType(): BelongsTo
{
return $this->belongsTo(BookType::class);
}
Here's the eloquent relationship reference from the Laravel docs: https://laravel.com/docs/9.x/eloquent-relationships
in migration for foreign key use: $table->foreignIdFor(BookType::class, 'book_type_id');
I am getting data from an external db which uses PascalCase for column names eg: ClientStatus,
I did write an accessor, but due to casing issue its not working.
public function getClientStatusAttribute() {
return $this->attributes['ClientStatus']."modified value";
}
You can either access it using ClientModel->client_status or override HasAttribute trait's two methods hasGetMutator() and mutateAttribute()
Laravel Model uses HasAttribute trait.
Model uses HasAttribute trait's two methods hasGetMutator() and mutateAttribute() to check if accessor exits or not and if exsits then return its value.
hasGetMutator() which by default uses studly helper method to make snake_case attribute to PascalCase
That's why we create accessor get{AttributeName}Attribute in this manner.So,make these methods something link this
public function hasGetMutator($key)
{
return method_exists($this, 'get'.$key.'Attribute');
}
protected function mutateAttribute($key, $value)
{
return $this->{'get'.$key.'Attribute'}($value);
}
Above we removed studly helper method, so when we try access a PascalCase attribute, first it will check if accessor exists, if yes then return its value.
I am trying to update/delete/create in belongsTo relations.
Company has many sports
sports is belonging to Company
Here is two models.
class CompanySports
{
public function company()
{
return $this->belongsTo(Company::class, "company_id","id");
}
class Company
public function sports()
{
return $this->hasMany(CompanySports::class,"company_id","id");
}
}
at controller, when sports is added or modified or remove, what is the best practice to update?
i know that many to many, sync can be used. In this, what is the best solution? Should i compare everytime after loading all from database which is not good practice i believe.
From your code, I would first recommend putting your models in separate files, and ensuring they are singular. If you use the artisan make:model command to generate the stubs, it should do this for you.
// app/CompanySport.php // <-- NOTE singular
class CompanySport // <-- NOTE singular
{
public function company()
{
return $this->belongsTo(Company::class, "company_id","id");
}
}
// app/Company.php
class Company {
public function sports()
{
return $this->hasMany(CompanySport::class,"company_id","id"); // singular
}
}
From there, I find it helpful to build helper methods in the various classes so that the grammar sounds natural and more importantly, belongs to the model. For example:
// app/Company.php
class Company
{
...
public function addSport(CompanySport $sport)
{
$this->sports()->save($sport);
}
public function removeSport(CompanySport $sport)
{
$this->sports()->find($sport->id)->delete();
}
}
These helper functions can then be easily called from anywhere, e.g. controller:
// CompanySportsController.php
public function store(Company $company, CompanySport $sport)
{
$company->addSport($sport);
return redirect('/company/' . $company->id);
}
If you are using these helpers, there is no comparing or sync to be done since you are only using a one to many relationship. Eloquent does everything for you.
Also, I've found this cheatsheet particularly helpful when building out the initial relationships and scaffolding of a new app.
While adding new record of Company Model, you need not to do anything as there is no child for it yet.
While updating an instance of a Company model, again you need not to update anything on its children. As relationship are based on id(primary key) which I believe you don't change while updating.
And now for deleting there are some questions. Do you want to delete the children when the parent is deleting? If so, you can use ON DELETE CASCADE which you can set up in migration like
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
in your spors table.
Well you can make your own function too like answered in here
Well if you don't want to delete the children, you can use softdelete on your Model. set up the relations then like
CompanySports
public function company()
{
return $this->belongsTo(Company::class, "company_id","id")->withTrashed();
}
This way you can get the parent of a children without any error though the parent is deleted.
I have a model A and a model B.
I made the relations between them, so i can do A->B->property.
But now I'm facing some problems.
I need to make a query and get only that B->property, not the B object.
So i use this:
A::with(['B'])->get()
But then a get a property called B in A with the complete B model.
Is there anyway to achieve something like this.
A::with(['B->property'])->get()
So then in the B property inside A I get the B->property instead the B object.
Is it possible?
PS: I can't use the query builder because i need that eloquent model.
I think this article will help you out:
http://laraveldaily.com/why-use-appends-with-accessors-in-eloquent/
You can put
$appends = ['property'] in your model to add a property field to your model.
Then, with an accessor method in the model you can describe how to populate that field (ie: with a field from another model via relationship).
It seems like that ought to give you what you want.
Try below code
In your A model:
protected $appends = ['property'];
public function B()
{
return $this->hasOne('\App\B');
}
public function getPropertyAttribute()
{
return $this->B()->property;
}
then A->property will give you B->property. Change model name and property name as per your requirement.
The answer by #shoieb0101 did not work for me because I had a belongsTo relationship rather than hasOne. If this is the case for you too, you just need to modify the accessor function as illustrated below.
protected $appends = ['property'];
public function B()
{
return $this->belongsTo('\App\B');
}
public function getPropertyAttribute()
{
return $this->B()->first()->property;
}
Note: I added ->first() in since belongsTo returns an array of results, but we can get the property from a single result only.
You should be able to constrain eagerloaded queries:
try
A::with(["B" => function($query){
$query->select('property');
}])->get();