I have collections of clients, and every collection can contain many clients. This PHP code loops collections, and clients inside every collection. And saves the client to the database.
foreach ($collections as $key => $collection) {
foreach ($collection as $k => $client) {
$name = $client['name'];
//...
$clientObj = new Client();
$clientObj->setName($name);
//..
$clientObj->save();
}
}
What I want to do, is to group every collection in one Mysql query, then go to the next collection. Because the previous code executes one query per client, And for performance, we need one query per collection.
How can we do that?
Add each record to a Doctrine_Collection the call save() on the collection object.
* Saves all records of this collection and processes the
* difference of the last snapshot and the current data
As Example:
$collection = new Doctrine_Collection('client');
$collection->add($client1);
$collection->add($client2);
$collection->save();
Related
I am facing issue with a scenario that I have to show data in two different grids present on same view. I don't want to query separately for both grids. What I want to achieve is to query only once and split data for both grids separately and pass it to both grids.
I have the option to hide rows on type basis but I don't want to use this
I have tried the option to hide rows on type basis but I don't want to use this option. I want something to split the main data provider into two data providers
The only way to do that with yii\data\ActiveDataProvider is extending it and overriding its prepareModels() and prepareKeys() methods.
Other option is to use yii\data\ArrayDataProvider instead.
//simple query just for illustration, modify it as you need
$all = MyModel::find()->all();
$first = $second = [];
foreach ($all as $item) {
// condition to decide where the current item belongs
if (someCondition) {
$first[] = $item;
} else {
$second[] = $item;
}
}
$firstProvider = new \yii\data\ArrayDataProvider([
'allModels' => $first,
]);
$secondProvider = new \yii\data\ArrayDataProvider([
'allModels' => $second,
]);
The main disadvantage of using ArrayDataProvider is, that you have to load all models into array even if you plan to use pagination. So if there are many rows in your table, it might be better to use two independent ActiveDataProvider and let them load the data in two queries.
I want to loop over a collection of items and attach a relationship based on if a particular condition is satisfied. Here is my code
public function bulkAssign()
{
$trainers = MasterTrainer::all();
for ($i=0; $i < count($trainers); $i++) {
$this->assignToManager($trainers[$i]);
}
// return redirect()->back()->with('success', 'Project Managers Assigned Successfully');
}
private function assignToManager($trainer)
{
$manager = ProjectManager::where('state', $trainer->state)->first();
return $trainer->update([
'project_manager_id' => $manager->id
]);
}
What I get is it attaches only the first manager to all the elements in the collection. What am i doing wrong?
can you inline the func for now? do some sort of echo/debugging?
but also I see several issues:
yes do use foreach because that is a bit better and you avoid having to use $i (making code a little more easy to read)
you are not attaching a relationship, you are setting a project_manager_id (i say this because initially i automatically thought you were going to dynamically add a relationship to model)
without knowing your db schema.. could you not do some sort of trick to avoid having to do this nth times?
$manager = ProjectManager::where('state', $trainer->state)->first();
you could either do:
$states = $trainers->pluck('states');
$managers = // do a query to get one trainer per state using group by
foreach ($trainers... ) {
$manager = $managers->where('state', $trainers->state)->first() // this is collection not eloquent
$trainer->update([
'project_manager_id' => $manager->id
]);
other would be to create a scope where you do a sub query to get manager id when u query for trainers
I have a code which fetches data from external API and commits it to DB afterwards:
protected function saveWidgetsToDatabase($widgetsDaily, Boost $boost, $date)
{
echo "Saving widgets to DB... ";
$widgets = Widget::all();
foreach ($widgetsDaily as $widgetDaily) {
$existingWidget = $widgets
->where('widget_id', $widgetDaily->id)
->where('date', $date)
->first();
if ($existingWidget === null)
$boost->widgets()->save(new Widget([
...
]));
else
$existingWidget->update([
...
]);
}
}
Relation I have is that one Boost has many Widgets. Now, the issue I'm facing is bottleneck DB saving/updating as I need to update a widget only if it has same date and ID, otherwise I need to create new one.
We are talking about few thousands of records, so I believe that where clauses are pretty intensive.
I wanted to make a batch save, though I didn't quite make it.
Are there any chances of making this faster?
When you call Widget::all();, that gets every single widget record in your database and creates a Widget instance for it. Therefore, $widgets will be a Collection of every Widget object stored in the database. If you have 10000 widget records, you'll have a Collection of 10000 Widget objects. This is obviously not what you want.
That also means that when you call $widgets->where()..., you're calling where() on the Collection object, which is using PHP to filter through the collection of objects, instead of using SQL to filter the database results.
There are a couple things you can do.
First, you know you only care about those widgets that have an id in the list of $widgetsDaily. So, limit your Widget query to only include those records that have a widget_id in that list of ids.
Second, add the date lookup to the database query as well.
Third, key the resulting collection by the widget_id field, so that you can directly access the item by the widget_id without having to loop through the entire collection looking for it every time.
protected function saveWidgetsToDatabase($widgetsDaily, Boost $boost, $date)
{
// Get the only widget_ids we care about (assumes $widgetsDaily is a collection)
$ids = $widgetsDaily->pluck('id')->all();
// Get the target widgets from the database. This collection will only
// contain widgets that we actually care about.
$widgets = Widget::whereIn('widget_id', $ids)
->where('date', $date)
->get()
->keyBy('widget_id'); // rekey the resulting collection
foreach ($widgetsDaily as $widgetDaily) {
// Because the collection was rekeyed on widget_id, you can use
// get(id) instead of having to use where('widget_id', id)->first()
$existingWidget = $widgets->get($widgetDaily->id);
if ($existingWidget === null)
$boost->widgets()->save(new Widget([
...
]));
else
$existingWidget->update([
...
]);
}
}
View in database I mean :
create view `vMaketType` as select * from MaketType
I have a view in database, but because of doctrine cant support it now, i using query, and fetch it one by one :
$em = $this->getDoctrine()->getManager();
$con = $this->getDoctrine()->getEntityManager()->getConnection();
$stmt = $con->executeQuery('SELECT * FROM vMaketType');
$domain = [];
//I must fetch it and set it one by one
foreach ($stmt->fetchAll() as $row){
$obj = new vMaketType();
$obj->setId($row["Id"]);
$obj->setName($row["Name"]);
$obj->setAmount($row["Amount"]);
array_push($domain, $obj);
}
for me this is really takes too much time to code one by one.
vMaketType is a custom entity I created to send data from controller to [Twig]view.
is there any easier way to fetch to array of object vMaketType?
because I have a view with 24 fields, I wish there is easier way for it.
Perhaps you can try with the serializer:
$obj = $this->get('serializer')->deserialize($row, 'Namespace\MaketType', 'array');
Code not tested, tweaks may be done, see the related doc.
First code:
$result=ResPlaces::model()->findall($criteria);
/*foreach($result as $value)
{
$model_name=ResRegistration::model()->findByAttributes(array('id'=>$value->user_id));
$model_image=ResImages::model()->findByAttributes(array('place_id'=>$value->id),array('limit'=>'1'));
}*/
}
echo CJSON::encode($result);
?>
i need to add model_name->company_name & $model_image->image
to my echo json array
Try to load the relations within the findAll so you don't need the foreach.
ResPlaces::model()->with('ResRegistration', 'ResImages')->together()->findAll($criteria);
For ResRegistration and ResImages use the relation names as defined in your model.
If you don't need all fields of these relations, you can specify a select in your $criteria.
See the guide for more info.
edit: I am not quite sure, why you cannot use relation. However you will need a loop, like you already have. Here is how you do it without relations:
First add the fields you want to use to your model. In this case
class ResPlaces extends CActiveRecord
{
public $name; // check that these do not collide with your models db fields
public $image;
[...]
}
In your controller do the loop like you did before:
foreach($result as $key => $value)
{
$result[$key]->name = ResRegistration::model()->findByAttributes(array('id' => $value->user_id));
// findByAttributes returns only one record, so you don't need the limit here
// if you want multiple records, you have to use findAllByAttributes
$result[$key]->image = ResImages::model()->findByAttributes(array('place_id' => $value->id), array('limit' => '1'));
}
That should do it. However I wouldn't recommend this way, because you have lots of additional database requests. If your $result is populated with say 100 records, you have in sum 200 additional queries which are not nessassary with a relation.
Note also that if you need these two other fields more often, it may be better to put these two queries which are now in the controller in your model. The afterFind() would be the right place.