Yii relations using variables of the active model - php

I have two rows in one of my tables which look like:
id product_id target_product_id description type
1 206587 456 sdfgdfgdfg 0
2 456 206587 fgdgfhghfgfdsgfdghfghfsd 0
When viewing the model for the row with id 1 I wish to get the second row based on where the product_id and the target_product_id are inversed. So I made a relation of:
'linked_product_relation' => array(self::HAS_ONE, 'Accessory', '',
'on'=>'linked_product_relation.target_product_id = product_id
AND link_product_relation.product_id = target_product_id')
However, it seems to only ever return null. I have checked that link_product_relation links to the table, and I get no SQL error, just a null return. If I use the relation with only link_product_relation.product_id = product_id though I do actually get a response, but only the row I am currently looking at. I seem to be missing something.
How can I get my desired output?
Edit
When I add a function to replace the relation:
function getTwinned(){
$a=Accessory::model()->findByAttributes(array('target_product_id'=>$this->product_id, 'product_id'=>$this->target_product_id));
if($a===null)
return null;
else
return $a;
}
It works perfectly.

You did not specify a foreign key ('' in your code). Try something like this:
'linked' => array(self::BELONGS_TO, 'Accessory', array(
'target_product_id'=>'product_id',
'product_id' => 'target_product_id',
)),
For more information also read the manual on this topic here and here.

Related

Laravel Eloquent - bulk update with whereIn array

I'm working on a project where I need to update many rows at once per coin Id.
in order to update all coins values, Im getting them all from the API, so for example I have back:
$coinsList= [[id="bitcoin", symbol="btc", name="Bintcoin"],[id="etherium", symbol="eth", name="Etherium"]];
and the database table columns is the following:
**| id | coin_id | symbol | name |**
now, I want to update all values to the database, according to the id only, so this is what I did:
// first get ids from my table
$exist_ids = Coinlist::all('coin_id')->pluck('coin_id')->toArray();
//get all ids to update (to ignore other ids):
$updatable_ids = array_values(array_intersect($exist_ids, $allCoinIds));//result for example is: array("bitcoin","etherium");
//and now, update the database:
Coinlist::whereIn('coin_id', $updatable_ids)
->update([
'symbol' => $coinsList[$key]['symbol'],
'name' => $coinsList[$key]['name'],
'updated_at' => now()
]);
the problem is, I don't have the "$key" in order to update the right row, what am I missing here?
Thanks!
Here is a good way to solve it:
in the beginning, I used this library: https://github.com/mavinoo/laravelBatch
to update many dynamic rows, but it was really slow, then thanks to Yasin, I moved to: https://github.com/iksaku/laravel-mass-update and now it works way better.
the implementation is simple, add a simple code to the Model class, then add:
User::massUpdate(
values: [
['username' => 'iksaku', 'name' => 'Jorge González'],
['username' => 'gm_mtz', 'name' => 'Gladys Martínez'],
],
uniqueBy: 'username'
);
while uniqueBy is the key for the row, and add other columns values to change them dynamically.

Laravel Eloquent get only one specific field with relationship and paginate

Here my code :
$prestations = Prestation::with('service:name','conciergeries.network:name')->orderBy($orderBy, $orderDirection)->simplePaginate(100);
$res = [
'results' => $prestations,
'total' => Prestation::all()->count(),
];
return $res;
I need to get only the network name and not all datas of "conciergeries" and "network" because actually is too heavy.
I have tried some things like
$prestations = Prestation::with('service:name','conciergeries.network:name')->orderBy($orderBy, $orderDirection)->pluck('network.name')->simplePaginate(100);
Thank you !
When using eager loading with specific columns, you should always include the id column and any relevant foreign key columns in the list of columns you wish to retrieve.
So try this instead:
$prestations = Prestation::with(
'service:id,name',
'conciergeries:id,network_id',
'conciergeries.network:id,name'
)
//->orderBy(...
Don't forget to add/modify any other relevant foreign keys you use, I just guessed these keys.

Yii2 search by foreign table field and total count

Im trying to search by a foreign table as follows:
2 tables:
people:
id
name
...
url:
id
peopleID
url
People.php model:
public function getUrls()
{
return $this->hasMany(Urls::className(), ['peopleID' => 'id'])->select(['url']);
}
PeopleSearch.php model:
...
$query->joinWith(['urls']);
...
$query
->andFilterWhere(['or',
['like', 'name', $this->name],
...
['like', 'url', $this->name]]
);
This works to search value entered in "name" field in several fields including foreign url one but in my view i enter a manual pagination by using something like:
$dataProvider->prepare();
if ($dataProvider->totalCount > 0)
echo Yii::t('app', 'Showing').": <b> ".($dataProvider->pagination->page*$dataProvider->pagination->pageSize+1)."-".($dataProvider->pagination->page*$dataProvider->pagination->pageSize+$dataProvider->count)."</b> ".Yii::t('app', 'of')." <b>".$dataProvider->totalCount."</b> ".Yii::t('app', 'items');
else echo Yii::t('app', 'No results found.');
echo LinkPager::widget(['pagination' => $dataProvider->pagination])
And $dataProvider->totalCount gives me the total amount of records from joined table but not the total records from people one. For instance if i have 2 records in people table and each one has 20 urls in "url" table index.php view shows "showing 1-2 of 40 items" instead of "showing 1-2 of 2 items"
Also LinkPager::widget shows a wrong number of total pages
Note that $dataProvider is passed from the controller to the view with
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
What could I do for pagination to perform the way i want?
Thank you in advance,
In People.php model my recommendation is to remove the ->select(['url']):
public function getUrls()
{
return $this->hasMany(Urls::className(), ['peopleID' => 'id']);
}
This way you can still manipulate those urls if needed.
In PeopleSearch.php model:
...
// $query->joinWith(['urls']);
// This line is the one that makes it so you get 20 results instead of 2, because you actually get one result for each url related to the people returned by the query.
$query->with(['urls']);
// This last line makes sure the model class populates the relation using only one query.
// Two queries will end up being executed to populate both the people and url models,
// however you will get the right amount for $dataProvider->totalCount.
...
if(strlen($this->url) > 0) {
$urlsPeopleIDs = \app\models\Url::find()
->select('peopleID')
->asArray()
->where(['like', 'url', $this->url])
->all();
$query->andWhere(['id' => $urlsPeopleIDs]);
}
// This way you will only filter by url when you receive a url string with lenght > 0.
// If you haven't already, you will need to create a public property called 'url' in your PeopleSearch.php model and add a 'string' or 'safe' rule so you can actually load it's value from post.

What does the $this->db->select portion of the CI Active Record API do?

I have code like this:
$this->db->select('title')->from('entries')->where('id', 1);
$query = $this->db->get();
echo $query->row('title');
Which echoes the title from the entries table where the id is equal to 1.
Why doesn't it work without the 'title in row function?
echo $query->row();
As it returns the first row?
Why do I have to have 'title' in both places ($query->row and $this->db->select), in order for this to work? It doesn't make sense to me.
Can anybody explain how this works, supposedly provide with alternative ways to get the value from the database?
$this->db->select('title')->from('entries')->where('id', 1);
Generates
SELECT title FROM entries WHERE id = 1
$query retrieves the result in an array:
array( [0] => array( [title] => 'your title' ))
row('title') returns the title column from the first row of your result array.
The reason you need to tell it which column to get is because row and get can be used with many columns.

HABTM with Pagination

At first take a look at the following model structure:
Model Building:
id
name
Model BuildingRange:
id
building_id
postalcode
Ok, so BuildingRange $belongsTo Building and Building $hasMany BuildingRange. Should be clear til' here.
Now let
$current_postalcode="12345";
I know want to do something like this in the BuildingController:
$this->paginate('Building',array('Building.BuildingRange.postalcode'=>$current_postalcode));
In text: I want to select all buildings for that an entry "BuildingRange" with $current_postalcode exists. How do you do that?
I appreciate your help!
When dealing with such a hasMany association, CakePHPs auto-magic needs two queries, one on the Building table, and one on the BuildingRange table. When passing conditions via the pagiante method, these conditions will be passed to the first query, and thus this it will fail since the associated models table isn't joined.
This problem can be solved on a few different ways, one would be using an ad-hoc join, for example:
$this->paginate = array
(
'joins' => array
(
array
(
'table' => 'building_ranges',
'alias' => 'BuildingRange',
'type' => 'LEFT',
'conditions' => array('BuildingRange.building_id = Building.id')
)
)
);
$this->paginate('Building', array('BuildingRange.postalcode' => $current_postalcode));
This would result in a query that looks something like this:
SELECT `Building`.`id`,
`Building`.`name`
FROM `buildings` AS `Building`
LEFT JOIN `building_ranges` AS `BuildingRange`
ON ( `BuildingRange`.`building_id` = `Building`.`id` )
WHERE `BuildingRange`.`postalcode` = '12345'
LIMIT 20
Note that in the conditions passed to the paginate method there is no need to reference the BuildingRange model through the Building model, ie no need to use Builduing.BuildingRange (that wouldn't work anyway).
ps, it's always good to mention the CakePHP version you are using!

Categories