laravel 5.4 polymorphic relation - eager read via query scope - php

I have a polymorphic relation where a table 'map_nodes' has a one-to-many to 'system_constants'.
I can't seem to use a query scope to eager load system_constants. No errors, just no records.
Here's my models. I've only included one model 'map_nodes':
namespace App\Modules\Olab\Models;
class SystemConstants extends BaseModel {
// Database:
// id INT(11) PK
// imageable_id INT(11)
// imageable_type VARCHAR(45)
// value BLOB
public function imageable() {
return $this->morphTo();
}
}
class MapNodes extends BaseModel {
public function scopeConstants( $query ) {
$query->with( [ 'SystemConstants' => function ( $query ) {
$query->select( 'system_constants.id',
'system_constants.value' );
} ] );
}
public function SystemConstants() {
return $this->morphMany('App\Modules\Olab\Models\SystemConstants', 'imageable');
}
}
This is what I have in system_constants table:
id, imageable_id, imageable_type, value
'1904', '25786', 'Nodes', <blob>
Note that I do have a Relation::morphMap defined, which allows me to use 'Nodes' in imageable_type.
This is what I'm running:
// returns collection of system_constant records
$temp = MapNodes::find(25786)->SystemConstants;
// doesn't work. get map_nodes record, but no eager load of system_constants
$temp = MapNodes::find(25786)->Constants()->first();
I have sql logging turned on and I see the correct SQL for both above queries. I can take that exact query in an SQL tool (with param substitution) and it works - it returns records:
select `system_constants`.`id`, `system_constants`.`value` from
`system_constants` where `system_constants`.`imageable_id` in (?) and
`system_constants`.`imageable_type` = ? - a:2:{i:0;i:25786;i:1;s:5:"Nodes";}
Any help appreciated.

I'm not sure, but this seems to have worked. I just removed the query scope and did an explicit 'with'.
$temp = MapNodes::with('SystemConstants')->find(25786);

Related

How to get only one note from relation belongsToMany?

I just have a table that has relation belongsToMany, BUT it was a mistake by developer so I can not change this structure SO I need to get only first(). However, when I take only first it return empty array but I need in object
$animals = Cat::query()->with(['types' => function($query) {
$query->first(); //wrong
}])
So how I can get only first? Because I need to order by this field and I can't because it is array
you can do this in two ways:
1- using hasOne relation:
class Cat {
public function firstType() {
return $this->hasOne(Type::class, 'type_id', 'id')->latest();
}
}
2- using staudenmeir/eloquent-eager-limit
after installing it you can write:
class Cat extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
public function firstType() {
return $this->hasMany(Type::class, 'type_id', 'id')->latest()->limit(1);
}
}
class Type extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
// ......
}
the advantage of HasEagerLimit trait is that you can limit the result not only to one but any number you want ...
now you can write:
$animals = Cat::query()->with('firstType');
You can add a attribute getter and set up into appends attribute. follow bellow example:
class Cat {
protected $appends = ['type'];
public function getTypeAttribute() {
// return the first element from your array of the belongsToMany relationship if it exists
return isset($this->types[0])? $this->types[0] : null;
}
}
That's important to remember this method will bring just one type. If you want to get the same type everytime, you create a diferente table where the cat table has the type_id column.
obs: Sorry for my english, it's still in working progress.

How do I return my OUTPUT clause in Laravel DB::insert

I am using Laravel and sqlsrv, connected to SQL Server 2016 and all is working great until I try to use an output clause in my insert query.
Query is something like
INSERT INTO TABLE(Columns) OUTPUT INSERTED.MyDesiredReturnColumn VALUES(Value)
This is working perfectly in SQL Server, and returning the desired value, but using Laravel's DB::insert functionality it is only returning a 1 (for successful insert)
I have a workaround that I would rather not have right now, using the CreatedOn field to return the most recently created row, but this has potential issues.
UPDATES: The field I am attempting to retrieve is a uniqueidentifier field (guid) that is created in SQL, not from Laravel-side
After attempting #PrathameshPalav's recommendation of using the Eloquent model creation, the values are being inserted correctly into the DB, but it is not returning the uniqueidentifier still.
$inserted = MyModel::create($information);
print "inserted id is " . $inserted->MyModelId;
This is printing "inserted id is "
Here is my model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class MyModel extends Model
{
//
protected $table = 'MyModelBase';
protected $primaryKey = 'MyModelId';
public $incrementing = false;
protected $keyType = "string";
public $timestamps = false;
protected $fillable = ['field1', 'field2', 'etc'];
}
Any ideas would be greatly helpful.
You can use Eloquent ORM for this purpose:
$insertedObject = ModelName::create($input_array);
It will return inserted model object in response. Or if you want only inserted record id then use
DB::table($tablename)->insertGetId($input_array);
The way that I solved this was by incorporating an Eloquent model (as pointed out by #PrathameshPalav), then (loosely) following this tutorial https://danielkoch.work/log/laravels-eloquent-guids.html
Specifically this part
public static function boot() {
parent::boot();
// Hook when a model is created
static::creating(function ($model) {
// Select a new ID
$result = DB::select( DB::raw('Select NewID() NewUUID') );
$model->{$model->getKeyName()} = $result[0]->NewUUID;
});
}
After that, I added the primary key I had defined to the $fillable array and tested, and it works =)
Thank you both for your help!
Yes, when you use insert it will return a bool. You can use insertGetId to get the the id.
If the table has an auto-incrementing id, use the insertGetId method
to insert a record and then retrieve the ID:
$data = [....]; // data that will be inserted
$id = DB::table('xxx')->insertGetId($data);
More info:
https://laravel.com/docs/5.5/queries#inserts

Laravel relationship returns NULL

I'm try to get relations via laravel Eloquement model. I'm have two models ex Books.php and Magazine.php
in Books.php i have
public function magazines()
{
return $this->hasMany('App\BOOKS', 'id', 'id');
}
Then i'm try to return all magazines, which relate with books
$books = Book::find(123);
$magazines = $books->magazines()->get();
return $magazines;
but i'm have NULL magazines! When i'm add
$magazines = $books->magazines()->toSql();
i'm see this select: select * from "MAGAZINE" where "MAGAZINE"."ID" is null and "MAGAZINE"."ID" is not null - what is it? Why laravel model put "is null and is not null"?
Notice, if i'm change
public function magazines()
{
return $this->belongsToo('App\BOOKS', 'id', 'id');
}
Select would be like this: select * from "MAGAZINE" where "MAGAZINE"."ID" is null
Anyone know, what is this?
There are several problems in your code:
HasMany takes as first parameter the related model class so in your case is pointless to pass App\Book. You should pass App\Magazine assuming that your magazine model is called Magazine
you are not saying to the HasMany relationship what is the name of the foreign key on your magazines table.
That should do the job
public function magazines()
{
return $this->hasMany('App\Magazine', 'book_id', 'id');
}
IMHO i suggest you to read the Laravel documentation before posting here, there are a lot of examples that will explain to you how to work with relationships and database tables. Take a look to One to Many relationships.
EDIT
You can omit the ->get() on the relationship and simply fetch your magazines doing
$magazines = $books->magazines;
You should try this:
Please change and try:
public function magazines()
{
return $this->hasMany('App\MAGAZINE');
}

use relationship in model accessor in laravel

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.

In Laravel 5.1, using Eloquent ORM, how does one update a single attribute for every relation?

I've been searching the documents everywhere and I can't figure this one out. Suppose I have set up a model with a hasMany relationship and the inverse like this:
class MasterAccount extends Model {
//master_account_id exists as a foreign key in sub_account table
//and references primary key in master_account table as
//defined in the migrations I set up for this
public function SubAccounts()
{
return $this->hasMany('App\SubAccount');
}
}
And I've made sure that some sub accounts have values matching the master account's primary id in the master_account_id column.
I've tested the relationship by dd()ing values in my controller like this:
public function edit(MasterAccount $masterAcct)
{
dd($masterAccount->subAccounts);
}
And this does successfully return a collection of all the related models. However, I cannot figure out how I would update a single attribute for every model that belongsTo MasterAccount -- shouldn't there be a way to cascade like this? I hacked it to work by doing this:
<?php namespace App\Http\Controllers;
use App\SubAccount;
public function update(MasterAccountRequest $request, MasterAccount $masterAccount)
{
//current request id
$id = $masterAccount->id;
//cascade global value to related Accounts
if ($request->some_value == 1)
{
//look more into this... ARGH!!!
SubAccount::where('master_account_id', '=', $id)->update(['this_value' => 1]);
}
}
And this works but I just KNOW there is some "Eloquent," way to do this.....right?
According to the documentation this should work:
<?php namespace App\Http\Controllers;
use App\SubAccount;
public function update(MasterAccountRequest $request, MasterAccount $masterAccount)
{
//current request id
$id = $masterAccount->id;
//cascade global value to related Accounts
if ($request->some_value == 1)
{
//look more into this... Wooooot!!!
$masterAccount->SubAccounts()->update(['this_value' => 1]);
}
}

Categories