Eager Loading return null in Laravel - laravel

I have a issue with Eager Loading
I have two models :
class Provider extends Model
{
/**
* Get the currency record associated with the provider.
*/
public function currency()
{
return $this->hasOne('App\Currency', 'tabcur', 'poucur')->where('tabcol','$DEV');
}
class Currency extends Model
{
/**
* Get the providers record associated with the currency.
*/
public function provider()
{
return $this->belongsTo('App\Provider', 'tabcur', 'poucur');
}
When i try this :
Provider::first()->currency
It's works
But, if i try this :
Provider::with('currency')->first()
The field currency is null
currency: null,
Can anyone help me ?
EDIT 1
I tried this
>>> DB::connection()->enableQueryLog()
>>> App\Provider::with('currency')->first()
>>> DB::getQueryLog()
I have this with App\Provider::with('currency')->first()
[
[
"query" => "select * from PROVIDER FETCH FIRST 1 ROWS ONLY",
"bindings" => [],
"time" => 18.74,
],
[
"query" => "select * from CURRENCY where tabcol = ? and CURRENCY.tabcur in (?)",
"bindings" => [
"$DEV",
" ",
],
"time" => 33.94,
],
]
But it's steel "null"

What first() do is:
The first method returns the first element in the collection that passes a given truth test.
So when the Eager loading test is passed, It returns the >>First Record<< with the relation loaded.
But
Your Relation currency is returning empty because the where condition in your currency relation is not passed so it will show that the the relation is loaded with with but it won't show any records because it didn't pass the where.
This happened because the first record that met the with condition did not pass the where condition, try to remove the where then load the relation again and see what is the value of tabcol, then do it again but this time with where but put the value you found for that returned record and it should work.

Provider::first()->currency is gonna return a Currency::class
Provider::with('currency')->first() will return a Provider::class with Currency::class inside the relations[]
I don't see any reason why your code would not work.

Related

How do I return the ID field in a related table in Laravel request

I have two related tables and I want to return all fields including the ID (key) field. My query below returns all fields except the ID. how do I return the ID field from one of the tables?
'programmes' => ProgrammeInstances::with('programmes')->get(),
the query below returns Unknown column 'programmes.programme_title' as it is looking for it in the table 'programme_instances'
'programmes' => ProgrammeInstances::with('programmes')->select('programmes.programme_title', 'programmeInstances.id', 'programmeInstances.name', 'programmeInstances.year')->get(),
Laravel provides multiple relationships, one of these is the hasMany() relationship which should return a collection where a User hasMany rows inside of your database
For example, inside your User model :
public function programmes() {
return $this->hasMany(Program::class);
}
Now in your controller, you can do :
public function edit($id) {
$programmes = User::find($id)->with('programmes')->get();
return view('user.edit')->with('programmes', $programmes);
}
And then you can loop over it inside your view
#forelse($programmes->programmes as $program)
// provide the data
#empty
// the user doesn’t have any programmes
#endforelse
a solution i found below - still not sure why ID isnt automatically returned when i get all fields, but works when i specify individual fields:
'programmes' => ProgrammeInstances::with('programmes')
->get()
->transform(fn ($prog) => [
'programme_title' => $prog->programmes->programme_title,
'id' => $prog->id,
'name' => $prog->name,
'year' => $prog->year,
]),

Filter an array list result from query builder in model in Laravel

I have a query function in my model to get list user and it return an array data to my repository class:
Model.php:
public function getUser()
{
return $this->select("{$this->table}.'*'")
->get();
}
Here is an example for the data:
[
[
"user_id":1,
"fullname":"amdv",
"is_active":0
],
[
"user_id":2,
"fullname":"abc",
"is_active":1
],
[
"user_id":3,
"fullname":"zyz",
"is_active":1
]
]
Now I want to check if is_active = 1, it continue check that user with other condition in other query
In my model, I have another query function with param is user id
Model.php:
public function checkUserAvailable($userId)
{
return $this->select("{$this->table}.'*'")
->join('other_table', 'join-condition')
->where('user_id', $userId)
->get();
}
But this function return data too, I don't know how to check this.
Thank you very much!
$data = [
[
"user_id":1,
"fullname":"amdv",
"is_active":1
],
[
"user_id":2,
"fullname":"abc",
"is_active":1
],
[
"user_id":3,
"fullname":"zyz",
"is_active":1
]
];
$filtered = collect($data)->filter(function($user) {
return User::someMethodYouMentioned($user['user_id']);
});
From what I understand is that you want to check the data from the array returned by the first function and if is_active = 1 then you want to check if the user is available via the second function in your model
//Get the user_id of records where is_active === 1
$available = collect($data)
->reject(fn($user) => $user['is_active'] !== 1)
->pluck('user_id')
->map(fn($id) => User::checkUserAvailable($id));
For this to work the checkUserAvailable needs to be a static function.
However looking at both the functions - question arises why do you need to run multiple database queries to check for user available via the second function.
You can modify the query in the second function to check for is_active
public static function checkUserAvailable()
{
return static::query()
->join('other_table', 'join-condition')
->where('is_active', 1)
->get();
}
Or if you still want to first get a list of all users as array and then check for availability, you could define the checkUserAvailable as a scope on the model
public function scopeCheckUserAvailable($query, $id)
{
$ids = Arr::wrap($id);
return $query->join('other_table', 'join-condition')
->whereIn('user_id', $ids);
}
Then you can get the data from the getUser() function and perform the second check like
//Get the user_id of records where is_active === 1
$checkIds = collect($data)
->reject(fn($user) => $user['is_active'] !== 1)
->pluck('user_id')
->all();
$availableUsers = User::checkAvailableUsers($checkIds)->get();
If you want to filter out the ones that are not active you can use array_filter with a callback:
$filtered = array_filter($array, fn ($i) => $i['is_active']);
If you want to use those values to call a function and then get those results you can use array_map:
$res = array_map([$this, 'checkUserAvailable'], array_column($filtered, 'user_id'));
You can also do this with the Collection methods if you would like:
collect($array)->where('is_active', 1)
->pluck('user_id')
->map([$this, 'checkUserAvailable'])
Though, I am not sure what else you need to be checking besides that is_active is true.

yii2 Validation not working

I need to validate a value from another table both of them field type decimal
if amount(model 1) moreover available(model2 DATA selected)
model 1
rule
public function rules()
{
return [
[['amount'], 'checkAvailable','skipOnEmpty' => false],
];
}
custom validation function
public function checkAvailable($attribute, $params)
{
$caid = $this->capex_budget_id;
$available = Capexbudget::find()
->select(['available'])
->where('id = :caid', [':caid' => $caid])
->all(); // select amount from Capexbudget where id = $caid
if ($this->amount > $available){
$this->addError('amount', 'not enough');
}
}
i can got a data available from Capexbudget where ID that i select
here are the query logs
but validation not working it not compare value between $this->amount and $available
what i missing please.
First of all, you're selecting all records matching your where conditions here:
$available = Capexbudget::find()
->select(['available'])
->where('id = :caid', [':caid' => $caid])
->all(); // HERE
You should change function all() to one(), to get one record.
Second thing is, if you're using all() or one(), that methods returns object, not value.
To make it work, you should change if statement to:
if ($this->amount > $available->available){ //here, compare to attribute `available`

Yii2 Activerecord get fields from junction table and order by according to them

I have items and units table that have many to many relationship. In other words, the item has many units and the unit has many items. I managed the relation through a junction table item_units. The junction table has some extra field more than item_id and unit_id, i.e it has price, and weight (it is an integer to manage the order of units for each item for display purposes).
I managed the relations in the models as follows:
//In Items model
/**
* #return \yii\db\ActiveQuery
*/
public function getItemUnits()
{
return $this->hasMany(ItemUnits::className(), ['item_id' => 'id'])->orderBy(['item_units.weight' => SORT_DESC]);
}
public function getUnits()
{
return $this->hasMany(Units::className(), ['id'=> 'unit_id'])->select(['id','title'])->via('itemUnits');
}
//
//In Units model
public function getItemUnits()
{
return $this->hasMany(ItemUnits::className(), ['unit_id' => 'id'])->orderBy(['price' => SORT_DESC]);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getItems()
{
return $this->hasMany(Items::className(), ['id' => 'item_id'])->via('itemUnits');
}
//
//In ItemUnits model
public function getItem()
{
return $this->hasOne(Items::className(), ['id' => 'item_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getUnit()
{
return $this->hasOne(Units::className(), ['id' => 'unit_id']);
}
In the controller I'm able to get the data of all related units to an item by something like the following:
$item = Items::findOne($id);
return Json::encode($item->units);
The following is a demo of the JSON object obtained:
[{"id":"4","title":"قرص"},{"id":"5","title":"شريط 10"},{"id":"6","title":"علبة 2 شريط"}]
However, I could not able to order the results according to the weight field in item_units table and also I could not able to include the price field there in the demo result above -JSON Object-.
I only able to get data in item_units as a separate result like the following:
return Json::encode($item->itemUnits);
Update
According to the two answers (#Александр Шалаев & #Onedev.Link) , I have overridden the fields method in Units model as follows:
public function fields() {
parent::fields();
return [
'price' => function($model){
return $model->id; //Here I could not able to get the corresponding price field value from item_units -junction table-
},
'id',
'title',
];
}
However, I could not able to get the price field value from the junction table, temporary, I set it to current model id to prevent error generation. Also, I still has no any mean to set order by using weight field in that junction table.
Update 2
In other words, how could Yii2 Activerecords perform the following SQL query:
SELECT units.id UnitID, units.title Unit, iu.weight, iu.price
FROM units
Left JOIN item_units AS iu
ON iu.item_id = 1 AND iu.unit_id = units.id
WHERE
units.id = iu.unit_id
ORDER BY iu.weight;
Finally I have found a solution. It depends on findBySql method. I'm going to use the above SQL query regarded in Update 2 -just I have removed some selected fields to be suitable for my current task-.
public function actionUnitsJson($id){
$sql = 'SELECT units.id, units.title
FROM units
Left JOIN item_units AS iu
ON iu.item_id = :id AND iu.unit_id = units.id
WHERE
units.id = iu.unit_id
ORDER BY iu.weight DESC;';
$units = \common\models\Units::findBySql($sql,[':id' => $id])->asArray()->all();
return Json::encode($units);
}
You need fields or extraFields in your ActiveRecord model with asArray.
Example:
/**
* #return array
*/
public function fields()
{
return [
'itemUnit', //will get getItemUnit method
];
}
or
/**
* #return array
*/
public function extraFields()
{
return [
'itemUnits', //it is relation name
];
}
Usage:
$model->toArray(); //will contains fields and extra fields relations
... sort array & return
By default, yii\base\Model::fields() returns all model attributes as fields, while yii\db\ActiveRecord::fields() only returns the attributes which have been populated from DB.
You can override fields() to add, remove, rename or redefine fields. The return value of fields() should be an array. The array keys are the field names, and the array values are the corresponding field definitions which can be either property/attribute names or anonymous functions returning the corresponding field values.

Symfony2 constraints entity validation yml

I've a many to many relation (user and account). In the user entity, I've private property groups (array collection).
When I try to validate this property (groups) with a simple "NotBlank", it's not work. So I try this below (collection and choice).
I read this http://symfony.com/fr/doc/2.3/reference/constraints/Choice.html
and this http://symfony.com/fr/doc/2.3/reference/constraints/Collection.html but it doesn't work or I don't correctly use them.
Can anybody gives me some help ?
/* USER accounts property
...
/**
* #ORM\ManyToMany(targetEntity="Account", mappedBy="users", cascade={"persist", "remove"})
*/
private $accounts;
...
Than the userType
...
->add('accounts', 'genemu_jqueryselect2_entity', array(
"class" => "CMiNewsBundle:Account",
"property" => "name",
"multiple" => "true",
"query_builder" => function (EntityRepository $er) use ($user)
{
return $er->createQueryBuilder('acc')
->join('acc.users','u')
->where('u.id = :userID')
->setParameter('userID' , $user);
}
)
)
...
The validation.yml
CM\Bundle\iNewsBundle\Entity\User:
properties:
...
accounts:
- NotBlank: ~
...
"NotBlank" assert checks if the property === null || property === ''. Since your property is a collection, you probably initialise it as an ArrayCollection in your constructor so it will never be null.
For collections you should use the "Count" assert
http://symfony.com/doc/current/reference/constraints/Count.html
It forces you to set the "maximum" count as well as the minimum so you might want to create your own assert.

Resources