I have a Work model where one of its attributes is the requestor, I am trying to obtain the requestor´s data (I can already obtain the primary key).
In this way I call the view:
$obras = Obra::all();
return view("obra.index", compact("obras"));
The view:
#forelse ($obras as $i)
<li>{{ $i->requestor_id->email }}</li>
#empty
The relationship in the work model:
public function requestor_id(){
return $this->hasOne(User::class);
}
Tables:
Users(applicants) have: id, name, email, password etc.
Work have: id, user_id, start_date etc.
The problem seems to be that I had the relations wrong, I had to use hasOne in the requester model (in the end I used hasMany because someone can create more than one work) and in the work model use belongsTo.
IMPORTANT note: the name of the function in the model cannot be the same as the name of a field in your table. Also in my case the column names do not follow the laravel/eloquent nomenclature so another parameter is added to belongsTo with the field name.
Work model:
public function solicitante(){
return $this->belongsTo(User::class, "requestor_id");
}
Requestor model:
public function obra(){
return $this->hasMany(Obra::class, "requestor_id");
}
And how to obtain requester data: $obra->solicitante->email
I think it's because of the name of the relation. You can try to renaming it to requestor. Laravel has some internal behavior runing on underscore. It might return only the id.
public function requestor()
{
return $this->hasOne(User::class);
}
import use App\Models\Obra; in the Controller.
use App\Models\Obra;
$obras = Obra::all();
return view("obra.index", compact("obras"));
Returned function must be same table name exm:
public function obra(){
return $this->hasMany(Obra::class, "requestor_id");
}
table name must be obra
Related
I'm trying to display a tag that is selected by the user. The tag name is in the tags table. The tagpost table has the mapping between the tag and the user.
The following is the User model, where the primary key in users table is id, and var_id can be many types and 2 is for users (not sure if the below where condition is correct):
public function tagpost()
{
return $this->hasMany('App\tagpost', 'var_id')->where('type',2);
}
The following is the tagpost model:
public function tags()
{
return $this->belongsToMany('App\tag','id');
}
public function users()
{
return $this->belongsToMany('App\User');
}
The following is the tag model :
public function tagposts()
{
return $this->hasMany('App\tagpost', 'var_id');
}
The following query is not working in the blade :
<option>{{ Auth::user()->tagpost()->tags()->select('t_name')->first()->t_name}} </option>
Error in blade :
Call to undefined method Illuminate\Database\Query\Builder::tags()
You are calling the relations as method whereas they should be called as property like this. Also i suggest you the laravel documentation
<option>{{ Auth::user()->tagpost->tags->select('t_name')->first()->t_name}} </option>
If tagpost is just the relation table why don’t you define a method tags() on the User model, using the tagposts table as the pivot so you can just do Auth::user()->tags()?
Your code won’t work because ->tagpost() returns the Relation itself and not the tagpost models...
SQL scheme:
bulletins
id increment
deals
id increment
seller_id
buyer_id
deals_items - items = bulletins
id increment
title
desc
bulletin_id
deal_id
How can I get deal row by bulletin id? In raw SQL it looks like:
select `deals`.* from `deals` inner join `deals_items` on `deals_items`.`deal_id` = `deals`.`id` where `deals_items`.`bulletin_id` = 10572
I tried:
public function deals()
{
return $this->hasManyThrough(DealItem::class,Deal::class, 'bulletin_id','dealid','id');
}
But it seems a wrong way. Can't find right way in laravel doc about relation.
#HCK shows right way.
but when I doing $bulletin->deals() in blade template I got empty collection of deals.
When just $bulletin->deal - all is fine, we have collection of deals.
I using protected $with = ['deals'] in my bulletin model, but what is different call method or property? Why with method empty result?
#Amarnasan was close, but the order of the foreign keys was wrong. Try this:
Deal.php
public function bulletins()
{
return $this
->belongsToMany(Bulletin::class, 'deals_items', 'deal_id', 'bulletin_id')
->withPivot('title','desc');
}
Bulletin.php
public function deals()
{
return $this
->belongsToMany(Deal::class, 'deals_items', 'bulletin_id', 'deal_id')
->withPivot('title','desc');
}
From the docs:
As mentioned previously, to determine the table name of the
relationship's joining table, Eloquent will join the two related model
names in alphabetical order. However, you are free to override this
convention. You may do so by passing a second argument to the
belongsToMany method:
return $this->belongsToMany('App\Role', 'role_user');
In addition to customizing the name of the joining table, you may also
customize the column names of the keys on the table by passing
additional arguments to the belongsToMany method. The third argument
is the foreign key name of the model on which you are defining the
relationship, while the fourth argument is the foreign key name of the
model that you are joining to:
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
Update
When you access the relationship as a method: $bulletin->deals() you are accessing the relationship itself. This will return an instance of \Illuminate\Database\Eloquent\Relations\BelongsToMany (in your case). Here the query is not executed yet, so you could keep adding constrains to your query, for example:
$bulletin
->deals()
->where('seller_id', 45) // <---
->skip(5) // <---
-> ... (And so on)
When you access it as a dynamic property, you are already executing the query, so this will return a Collection instance. Is the same as calling the relationship as a method and then attach the ->get() at the end, so this two are equivalent:
$bulletin->deals()->get()
// equals to:
$bulletin->deals
Check this other answer, it answers your question.
DealClass:
public function bulletins()
return $this->belongsToMany('App\Bulletin', 'deals_items ', 'bulletin_id', 'deal_id')->withPivot('title','desc');
}
BulletinClass:
public function deals()
return $this->belongsToMany('App\Deal', 'deals_items ', 'deal_id', 'bulletin_id')->withPivot('title','desc');
}
deals model -
public function bulletins()
return $this->belongsToMany(Bulletin::class, 'deals_items ', 'bulletin_id', 'deal_id');
}
bulletin model:-
public function deals()
{
return $this
->belongsToMany(Deal::class, 'deals_items', 'deal_id', 'bulletin_id',);
}
I have a BuildingImage model with a OneToOne relation to BuildingType:
BuildImage Model:
/**
* Get the source type of the Building Image.
*/
public function type()
{
return $this->hasOne('App\BuildingType');
}
BuildingType Model:
/**
* Get the Building Image that owns the building type.
*/
public function buildingImage()
{
return $this->belongsTo('App\BuildingImage');
}
My tables:
building_images table -> source is the building type id
building_types table
When I try to do this in my controller just to test:
(an ImageRequest has one or more Builings and a Building has one BuildingType)
$imageRequest = ImageRequest::findOrFail($id);
$buildings = $imageRequest->buildingImages;
foreach ($buildings as $building) {
dd($building->type);
}
I get this error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'building_types.building_image_id' in 'where clause' (SQL: select *
from building_types where building_types.building_image_id = 45
and building_types.building_image_id is not null limit 1)
What am I doing wrong here?
That's because by default laravel will look for a primary key named {model}_id, and given that you are using a different column name (source), you need to specify when defining the relationship:
As the documentation states:
Eloquent determines the foreign key of the relationship based on the model name. In this case, the Phone model is automatically assumed to have a user_id foreign key. If you wish to override this convention, you may pass a second argument to the hasOne method:
return $this->hasOne('App\Phone', 'foreign_key');
Additionally, Eloquent assumes that the foreign key should have a value matching the id (or the custom $primaryKey) column of the parent. In other words, Eloquent will look for the value of the user's id column in the user_id column of the Phone record. If you would like the relationship to use a value other than id, you may pass a third argument to the hasOne method specifying your custom key:
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
Now that that is clear. Let's talk about the relationship itself.
You are defining that a BuildImage has one BuildingType. But with that logic, the foreign key should be stored in the building_types table, and not the other way around (source column appears in the building_images table). And -I'm just assuming that- many BuildImage can belongs to an specific BuildingType. So, if this assumption is correct:
a BuildImage belongs to a specific BuildingType.
a BuildinType can be specify in many BuildImages
So, you should define your relationship like this:
BuildImage.php
public function type()
{
return $this->belongsTo('App\BuildingType', 'source');
}
BuildingType.php
public function images()
{
return $this->hasMany(BuildingImage::class, 'source');
}
Your BuildImage model should be
/**
* Get the source type of the Building Image.
*/
public function type() {
return $this->hasOne('App\BuildingType',"id","source");
}
And BuildingType Model should be
/**
* Get the Building Image that owns the building type.
*/
public function buildingImage()
{
return $this->belongsTo('App\BuildingImage',"source","id");
}
This should work.
For more info have a look
https://laravel.com/docs/5.7/eloquent-relationships#one-to-one
Have you tried to indicate the index ID like this?
public function buildingImage()
{
return $this->belongsTo('App\BuildingImage', 'image_request_id');
}
Eloquent determines the foreign key of the relationship based on the
model name. In this case, the Phone model is automatically assumed to
have a user_id foreign key. If you wish to override this convention,
you may pass the second argument to the hasOne method:
return $this->hasOne('App\Phone', 'foreign_key');
https://laravel.com/docs/5.7/eloquent-relationships#one-to-one
I have set up a hasMany relationship between a model called Supplier, and a model called SupplierMeta.
SupplierMeta is linked to a table with the structure:
id, supplier_id, name, value
and the Supplier model has the following relationship defined:
public function meta() {
return $this->hasMany('Model\SupplierMeta');
}
The relationship itself works fine, but what I would like to do is define another function that searches within that relationship by the "name" field and returns the value. I would like it to work in this format:
$supplier->meta->field_name, or $supplier->meta()->field_name
This would either return the "value" field of the relevant SupplierMeta object, if it exists, or otherwise return false. Is this possible?
If you want to find suppliers by meta name, use whereHas() method:
Supplier::whereHas('meta', function ($q) use($metaName) {
$q->where('name', $metaName);
})->get();
Here's an example macro you can use, which enables you to do $supplier->meta->meta('field_name');
Collection::macro('meta', function ($name) {
return $this->first(function ($item) use ($name) {
return $item->name == $name;
});
});
You could then add a getMeta() method to your Supplier model:
public function getMeta($name) {
return $this->meta->meta($name);
}
This would let you do $supplier->getMeta('field_name');
I'm not totally sure how to get the last part of using it as a property like $supplier->meta->some_field however, this is a bit too magical for me.
I think you want to implement some __get() method, or a getMetaAttribute() on your model. Not totally sure on that part.
Suppose I have a Course model like this :
class Course extends Model
{
public $primaryKey = 'course_id';
protected $appends = ['teacher_name'];
public function getTeacherNameAttribute ()
{
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
}
public function teacher ()
{
return $this->belongsTo('App\User', 'teacher', 'user_id');
}
}
And in the other hand there is a User model like this :
class User extends Authenticatable
{
public $primaryKey = 'user_id';
protected $appends = ['full_name'];
public function getFullNameAttribute ()
{
return $this->name . ' ' . $this->family;
}
public function course ()
{
return $this->hasMany('App\Course', 'teacher', 'user_id');
}
}
As you can see there is a hasMany relationship between those.
There is an full_name accessor in User model.
Now I want to add a teacher_name accessor to Course model that uses it's teacher relations and gets full_name of teacher and appends to Course always.
In fact I want whenever call a Course model, it's related teacher name included like other properties.
But every time , when call a Course model , I got this error :
exception 'ErrorException' with message 'Trying to get property of non-object' in D:\wamp\www\lms-api\app\Course.php:166
That refers to this line of Course model :
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
I do not know how can I solve that and what is problem exactly.
Yikes some interesting answers here.
FYI to those coming after me- getFooAttribute() should return the data, and not modify the internal attributes array.
If you set a new value in the attributes array (that doesnt exist in this model's db schema) and then attempt to save the model, you'll hit a query exception.
It's worth reading up the laravel docs on attribute accessors/mutators for more info.
Furthermore, if you need to access a related object from within the model (like in an accessor) you ought to call $related = $this->getRelation('foo'); - note that if the relation isnt loaded (e.g., if you didnt fetch this object/collection with eager loaded relations) then $this->getRelation() could return null, but crucially if it is loaded, it won't run the same query(ies) to fetch the data again. So couple that with if (!$this->relationLoaded('foo')) { $this->loadRelation('foo'); }. You can then interact with the related object/collection as normal.
$this->attributes['teacher_name'] = $this->teacher()->first()->full_name;
Should be
$this->attributes['teacher_name'] = $this->teacher->full_name;
First thing is that you want to reference the relationship, so loose the brackets (), and because the relationship is belongsTo, you will have one user / teacher returned. So you don't need the first().
We haven't seen your fields but probably you will have to change:
return $this->belongsTo('App\User', 'teacher', 'user_id');
to
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
where foreign_key and other_key are the primary keys that you need to make the join on.
Check this link from the documentation for reference:
https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse
the right way to do this is:
COURSE
public function setTeacherNameAttribute ()
{
$this->attributes['teacher_name'] = $this->teacher->full_name;
}
100% working for me.
I have one to one relationship between Order and Shipment. I have to add the accessor of shipments table column from orders table.
function getOrderNoAttribute()
{
$appendText = "OR100";
if($this->orderShipment()->first()) {
$appendText = $this->orderShipment()->first()->is_shipping === 1 ? "ORE100" : "OR100";
}
return $appendText . $this->attributes['id'];
}
This error is only object data to array use or array data to object data use.
example::
$var->feild insted of $var[feild]
$var[feild] insted of $var->feild
You should use return for accessors . something like this :
public function getTeacherNameAttribute ()
{
return $this->teacher()->first()->full_name ?? '';
}
maybe a course hasn't teacher.