hasMany relationships return Undefined property - laravel

I like to list all MovimentoProdutoUnidade that movimento_id = 3 using the hasMany function.
My Model Movimento:
use Illuminate\Database\Eloquent\Model;
use App\Unidade;
class Movimento extends Model
{
protected $fillable = [
"movimento", "descricao", "requisitante", "despachante", "data", "unidade_ori_id", "unidade_des_id"
];
protected $table = "movimentos";
public function movimentoProdutoUnidade(){
return $this->hasMany('App\MovimentoProdutoUnidade', 'movimento_id');
}
}
My Model MovimentoProdutoUnidade
use Illuminate\Database\Eloquent\Model;
use App\Movimento;
class MovimentoProdutoUnidade extends Model
{
protected $fillable = [
"movimento_id", "unidadeProduto_id"
];
protected $table = "movimento_produtounidades";
public function movimento(){
return $this->belongsTo('App\Movimento', 'movimento_id');
}
}
My Controller:
public function licitacao(Request $request){
$movimentos = Movimento::where('unidade_ori_id', 3)->movimentoProdutoUnidade;
dd($movimentos);
//return view('relatorios.licitacao', compact('movimentos'));
}
The dd fuction return
Undefined property: Illuminate\Database\Eloquent\Builder::$movimentoProdutoUnidade

Your error is because you're not calling first() on the query builder object, so you have an instance of Builder (which does not have a $movimentoProdutoUnidade property) instead of a Movimento model:
$movimento = Movimento::where('unidade_ori_id', 3)->first();
$movimento_produto_unidade = $movimento->movimentoProdutoUnidade;
However, if you want all MovimentoProdutoUnidade, try thinking "backwards":
$movimento_produto_unidade = MovimentoProdutoUnidade::whereHas('movimento', function ($query) {
return $query->where('unidade_ori_id', 3);
})
->get();

As stated in the comment i made, try using first function like this:
Movimento::where('unidade_ori_id', 3)->first()->movimentoProdutoUnidade;
Remember always after the condition use get(), first() or find() functions to pull the data from the database.
Take a look to this link

Related

In Laravel - Add a variable to a model, without putting it in the database

I have a Team-model that has been used several places, and which returns the fields from the database in an API-endpoint.
It's currently accessed and returned like this:
$team = Team::find(1)
return $team;
I would like to add a calculated variable to that returned Collection. I imagined that I could add it to the constructor of the Model, and thereby get it with all the places where the Team-model is currently used, like this:
class Team extends Model
{
protected $table = 'teams';
protected $fillable = [
'id',
'created_at',
'updated_at',
'team_name'
];
public $number_of_players;
public function __construct( array $attributes = [] ){
$this->number_of_players = 3; //This number should be calculated
parent::__construct( $attributes );
}
}
But that doesn't work.
How do I add a variable to all the places, where the Team-model is fetched?
I also looked into API Resources. I looks like that that could be a solution, but I found it pretty verbose and a long-haired solution (plus, I couldn't get it to work either).
You can use accessor/mutator
Suppose you have a relationship
Team->Player (Team hasMany Players)
You can do like
in Team model
class Model extends Model {
public function players()
{
return $this->hasMany(Player::class, 'team_id', 'id');
}
}
now you can make it
<?php
class Model extends Model {
protected $appends = ['number_of_players'];
public function players()
{
return $this->hasMany(Player::class, 'team_id', 'id');
}
public function getNumberOfPlayersAttribute()
{
return $this->players->count();
}
}
And then access the players count of a team like App/Team::find(1)->number_of_players

How can you use Eloquent to find related values two tables away and in a different database?

I am developing a system that extends an existing ERP system, and so is accessing two databases (both on the same MS SQL Server). I am trying to access items on the "Equipment" model (this is a table in the ERP database) through the "EquipmentInstance" model from the "EquipmentType" model (these two are in the new database). They are related as per this diagram:
The three models are as follows:
EquipmentType
namespace App;
use Illuminate\Database\Eloquent\Model;
class EquipmentType extends Model
{
protected $table = 'dbo.EquipmentType';
protected $connection = 'sqlsrv';
protected $primaryKey = 'EquipmentTypeID';
protected $fillable = [
'TypeName',
'ProductManager'
];
public function EquipmentInstance()
{
return $this->hasMany(EquipmentInstance::class,'EquipmentTypeID', 'EquipmentTypeID');
}
public function Equipment()
{
return $this->hasManyThrough(
Equipment::class,
EquipmentInstance::class,
'TypeID',
'PartNum',
'TypeID',
'PartNum'
);
}
}
EquipmentInstance
namespace App;
use Illuminate\Database\Eloquent\Model;
class EquipmentInstance extends Model
{
protected $table = 'dbo.EquipmentInstance';
protected $primaryKey = 'EquipmentID';
protected $keyType = 'string';
protected $connection = 'sqlsrv';
protected $fillable = [
'EquipmentID',
'EquipmentTypeID',
'PartNum'
];
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Equipment()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
public function EquipmentType()
{
return $this->belongsTo(EquipmentType::class); /*,'EquipmentTypeID', 'EquipmentTypeID'*/
}
/* public function Attribute()
{
return $this->belongsTo(Equipment::class,'SerialNumber', 'JobNum');
}
public function TechNote()
{
return $this->belongsTo(Equipment::class,'SerialNumber', 'JobNum');
}*/
}
Equipment
namespace App;
use Illuminate\Database\Eloquent\Model;
class Equipment extends Model
{
protected $table = 'ERP.SerialNo';
public $timestamps = false;
protected $primaryKey = 'SerialNumber';
protected $keyType = 'string';
protected $connection = 'epicor';
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Customer()
{
return $this->belongsTo(Customer::class,'CustNum', 'CustNum');
}
public function Equipment()
{
return $this->belongsTo(Equipment::class,'SerialNumber', 'JobNum');
}
public function EquipmentInstance()
{
return $this->belongsTo(EquipmentInstance::class,'PartNum', 'PartNum');
}
}
On the EquipmentType Controller I am trying to get all of the Equipment through the EquipmentInstance so for each EquipmentInstance I can display all of the Equipments.
EquipmentType Controller
public function show(EquipmentType $EquipmentType)
{
$EquipmentInstance = $EquipmentType->EquipmentInstance()
->get();
$Equipments = $EquipmentType->EquipmentInstance()->Equipment()
->get();
return view('EquipmentType.show', compact('EquipmentType', 'EquipmentInstance', 'Equipments'));
}
The error message I get is
"BadMethodCallException
Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::Equipment()"
I believe the issue is that (my understanding is rocky) that Eloquent is trying to write one query to access both databases, which is failing. However I am not sure how to proceed.
Any help would be greatly received.
Richard
Update
I have implemented what gbalduzzi suggested in his answer, which almost worked, and I am sure the issue is with my blade implemtention. I have nested two forloops:
#foreach($EquipmentType->EquipmentInstance as $EquipmentInstance)
#foreach($Equipments as $Equipment)
<tr>
<td>{{$EquipmentInstance->PartNum}} - {{$EquipmentInstance->Part->PartDescription}}</td>
<td>{{$Equipment->SerialNumber}}</td>
<td>{{$Equipment->SNStatus}}</td>
<td>{{--{{$Equipment->Customer->LegalName}}--}}</td>
</tr>
#endforeach
#endforeach
Which is displaying the serial numbers (from the Equipment model) for the first EquipmentInstance only and repeating them for all EquipmentInstanced.
Update 2
I have proven that the issue is with first() in the suggested answer, as if I change this to last() the results change as you would expect (see update 1). So my question now is:
Is there an equivelant of first(), last() which is all() or every()?
The problem is not in your database configuration, but in the way you are calling the relationship. Instead of:
$Equipments = $EquipmentType->EquipmentInstance()->Equipment()
->get();
use:
$Equipments = $EquipmentType->EquipmentInstance->first()->Equipment()
->get();
Long answer
In Eloquent, you can use a relationship in 2 ways:
as a magic field (i.e. $EquipmentType->EquipmentInstance). In this case you get as a result an instance of the model EquipmentInstance (also, if you already queried it, it directly returns the value without executing a new query)
as an eloquent query (i.e. $EquipmentType->EquipmentInstance()). Using it as a function, you don't get the model but a RelationShip instance, that is basically an eloquent query and can be chained with other eloquent methods, such as where, orderBy, ecc
So, if you call $EquipmentType->EquipmentInstance()->Equipment() it throws an error because the eloquent query does NOT have the relationship Equipment().
On the other hand, $EquipmentType->EquipmentInstance->Equipment works because it calls Equipment on the actual model instance, that has the Equipment relationship properly defined.

Custom attribute crashing on toJSON

I am trying to determine which position the order is in to generate a order id, but this crashes laravel, nothing in the logs, just a 500 error in the browser:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Load extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $appends = ['order_no'];
public function workorder()
{
return $this->belongsTo('App\WorkOrder', 'work_order_id');
}
public function getOrderNoAttribute()
{
$count = 1;
foreach ($this->workorder->loads as $load) {
if ($load->id == $this->id) {
break;
}
$count++;
}
return $this->workorder->id . "-" . $count;
}
}
When I changed it to return just an integer it worked, so I am almost certain it is the relation access causing the issue. Is there a way to do this that is better?
Generally while defining calculated attributes, dependence on relationship should be avoided. So one way to achieve what you are trying is (as you mentioned solved) is to get all loads having the same work_orderid and proceed with it.
public function getLoadCountAttribute ()
{
$id = $this->work_order_id;
return static::where('work_order_id', $id)->count();
}
Another way, more logical I guess, would be to define a relationship and eager load
//define a relation in your Load model
public function load_count ()
{
return count($this->workorder->loads)
//-1 if you want to exclude the current load from count
;
}
//Then use Load::with('load_count') to eager load the load_count
//You may also use global scope
Yet another way would be to define a static function on Workorder model, which will accept an id and return the load_count
//Workorder model
public static function getLoadCount($id)
{
$workorder = static::findOrFail($id);
return count($workorder->loads);
}
Hope this helps.

call collection with other model to lists

How do I call the relational data in the statement below using a with statement.
$suppliers = Supplier::with('user')->lists('user.company', 'user.id'); // doesn't work
class Supplier extends Model
{
protected $table = "suppliers";
protected $fillable = ['email'];
public function user() {
return $this->belongsTo('App\User', 'email', 'email');
}
}
You achieve your goal using the pluck method:
Supplier::with('user')->get()->pluck ('user.company', 'user.id');
The get method returns a Collection, then you can use its methods.

Laravel Mutators with Constructor?

Ill have a problem because my mutators never get called when ill use an constructor:
Like this:
function __construct() {
$this->attributes['guid'] = Uuid::generate(4)->string;
}
public function setDateAttribute($date) {
dd($date); // Never gets called
}
Ill already found out, that the mutators would ne be called when ill use an constructor, so i should use:
public function __construct(array $attributes = array()){
parent::__construct($attributes);
$this->attributes['guid'] = Uuid::generate(4)->string;
}
public function setDateAttribute($date) {
dd($date); // now its getting called
}
But so ill get the following error:
array_key_exists() expects parameter 2 to be array, null given
But i dont know where? Can anyone help me out how to create a default value (like a UUID) for a specific column, and use mutators in the same class?
Edit: Thanks Martin Bean for your help, but i am now getting the following error:
Cannot declare class App\Uuid because the name is already in use
I have tried:
Creating a File called "Uuid.php" in /app/ -> /app/Uuid.php
With this content:
<?php namespace App;
use Webpatser\Uuid\Uuid;
trait Uuid
{
public static function bootUuid()
{
static::creating(function ($model) {
$model->uuid = Uuid::generate(4)->string();
});
}
}
Changed my Model to:
<?php namespace App;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
class Task extends Model {
use \App\Uuid;
Thank you very much!
Edit 2:
Ill tried it this way:
class Task extends Model {
protected $table = 'tasks';
protected $fillable = ['..... 'date', 'guid'];
public function setGuidAttribute($first=false){
if($first) $this->attributes['guid'] = Uuid::generate(4)->string;
}
TaskController:
public function store() {
$input = Request::all();
$input['guid'] = true;
Task::create($input);
return redirect('/');
}
Works fine, but when ill use:
public function setDateAttribute(){
$this->attributes['date'] = date('Y-m-d', $date);
}
In Task.php ill get:
Undefined variable: date
EDITED:
based on your comment:
i would like to set a field on first insert
use Uuid; //please reference the correct namespace to Uuid
class User extends Model{
protected $fillable = [
'first_name',
'email',
'guid' //add guid to list of your fillables
]
public function setGuidAttribute($first=false){
if($first) $this->attributes['guid'] = Uuid::generate(4)->string;
}
}
Later:
$user = User::create([
'guid' => true, //setAttribute will handle this
'first_name' => 'Digitlimit',
'email" => my#email.com
]);
dd($user->guid);
NB: Remove the __construct() method from your model
Mutators are called when you try and set a property on the model—they’re invoked via the __get magic method. If you manually assign a property in a method or constructor, then no mutators will ever be called.
Regardless, you should not be creating constructors on Eloquent model classes. This could interfere with how Eloquent models are “booted”.
If you need to set an UUID on a model then I’d suggest using a trait that has its own boot method:
namespace App;
trait Uuid
{
public static function bootUuid()
{
static::creating(function ($model) {
$model->uuid = \Vendor\Uuid::generate(4)->string();
});
}
}
You apply the trait to your model…
class SomeModel extends Model
{
use \App\Uuid;
}
…and now each time a model is created, a UUID will be generated and stored in the database with your model.

Resources