I'm encountering an issue because I'm sure I'm not doing this correctly with my programming. I have created a custom model in Magento.
In the database table of my model there are several entities with the same attributes...
I need to pick just one from all these entities with the same attribute that I have. For the moment I did this:
$myvariable = Mage::getModel('test/test')->getCollection()
->setOrder('idserialkeys', 'asc')
->addFilter('idproduit', 1)
->addFilter('utilise', 0)
->addFilter('customerid', 0)
->addFilter('numcommande', 0)
From this loading I have around a hundred results but I need to update only one of these, so just after I'm doing:
->setPageSize(1);
The problem is that I need a foreach after to update my entity
foreach($mavaribale as $modifiemoi) {
// Update of my entity because of course there is only one
}
As you can see I'm obliged to do a loop (for each) even if I have a setPagesize... I would like to avoid this loop to optimize my code.
When you have a collection, and you only need one element, use the getFirstItem method. Try this:
$modifiemoi = $myvariable->getFirstItem();
Make sure that you also use your setPageSize call so that you only transfer data for one item.
All collections are Varien_Data_Collection objects so you can use getFirstItem:
$modifiemoi = $mavaribale->getFirstItem();
Related
I have some instances where a eloquent is resulting an single array not a collection. Although dd shows it as a collection with a single entry.
For example I have a query in a controller:
$pg = Page::with('getPanels')->where('slug',$slug)->get();
This will return a single result and works fine, so I pass this to a blade template. My complete function is
$pg = Page::with('getPanels')->where('slug',$slug)->get();
return view('front.page',['pg' => $pg]);
As soon as he template is brought in it will fall over at
if (!is_null($pg->headImage))
{$img = asset('images/pages')."/".$pg->headImage;}
and I will get
Property [headImage] does not exist on this collection instance.
If I change the line to
if (!is_null($pg[0]['headImage']))
it will continue OK. This is of course a pain as I would much rather use $pg->headImage.
Can someone enlighten me please?
I have sorted this and I hope it will help other people.
If I use
$pg = Page::with('getPanels')->where('slug',$slug)->first();
it will be just one result (naturally) and therefore
$pg->headImage
will fail as it wants
$pg[0]['headImage']
but if I change the eloquent instead of get(0 to first() (still just one result)
$pg = Page::with('getPanels')->where('slug',$slug)->first();
I can use $pg->headImage or what field I want.
I was wondering what's the best way to destroy multiples database entries with ELOQUENT and I don't find a way to determine that.
So I have 3 array of id's (2 with ints, 1 with strings).
Is it better to go with a foreach and ->delete() every entry or destroy the array ?
When I look at the destroy function, it states the following :
We will actually pull the models from the database table and call
delete on each of them individually so that their events get fired
properly with a correct set of attributes in case the developers
wants to check these.
And the code clearly shows :
$key = $instance->getKeyName();
foreach ($instance->whereIn($key, $ids)->get() as $model) {
if ($model->delete()) {
$count++;
}
}
So I guess there's no real difference and the destroy function is just to avoid the use of a foreach. Can anyone confirm or inform and explain ?
Thanks :)
At first you need to know the difference between destroy and delete, destroy is think to be used for removing an entity (object/model) and delete for being used on a query builder.
Both are different ways but they have the same purpose you can do like:
Model::destroy(array(1, 2, 3));
or
$ids = explode(",", [1,2,3]);
$model->find($ids)->each(function ($model, $key) {
//Do things before deleting
$model->delete();
});
But as you can see the first one is just more direct, on the second one you can do custom things before deleting.
I'm trying to exclude products from populating the search result.
It seems to be working fine on my localhost but not on clients dev server.
I'm observing the event
catalog_block_product_list_collection
and in observer method, in the end is this code:
$observer->getCollection()
->addFieldToFilter('entity_id', array('nin' => array_keys($_excludeProducts)))
->clear()
->load();
which works for catalog as well and search result list but for the moment not on search result list on clients dev server.
Any guidance/help is greatly appreciated.
Edit: Debugging this method gives me an empty collection but still the data is populating from somewhere.
I've changed the approach and used another event: catalog_product_collection_load_before
found better method to implement the approach with less code. #optimization
$excludeIds = array(2,3,4); //$excludeIds mixed
$observer->getCollection()
->addIdFilter($excludeIds, true); //exclude = true
The event also helps in keeping the correct items count on toolbar as it is dispatched before collection load.
I ran into a similar issue when trying to filter this collection using this event.
Do you have flat categories and flat products set the same in both environments? In my case, my code would only work with flat tables OFF, since I was using joining other EAV attributes.
In your case, if you are using flat, I think you just need to do addAttributeToFilter() instead.
In my case, here is what my observer looks like:
function onCategoryCollectionLoad($observer) {
$collection = $observer->getEvent()->getCategoryCollection();
$customerGroupId = (int) Mage::getSingleton('customer/session')->getCustomerGroupId();
// hidden_from_groups is an EAV attribute; I couldn't figure out how to make it work with flat because it has a backend_model
$collection->addAttributeToSelect('hidden_from_groups');
$collection->addExpressionAttributeToSelect('should_be_hidden', "COALESCE(FIND_IN_SET($customerGroupId, {{attribute}}), 0)", 'hidden_from_groups');
// should_be_hidden is not a real db field (nor EAV attribute); it only exists because of the addExpressionAttributeToSelect() above.
$collection->addFieldToFilter('should_be_hidden', array('lt' => 1));
// I don't call $collection->load(); that will get called further down the line.
}
I'm working in Laravel 4, and I have a Child model with multiple EducationProfiles:
class Child extends EloquentVersioned
{
public function educationProfiles()
{
return $this->hasMany('EducationProfile');
}
}
If I wanted to get all the EducationProfiles for each kid under age 10 it would be easy:
Child::where('date_of_birth','>','2004-03-27')->with('educationProfiles')->all();
But say (as I do) that I would like to use with() to grab a calculated value for the Education Profiles of each of those kids, something like:
SELECT `education_profiles`.`child_id`, GROUP_CONCAT(`education_profiles`.`district`) as `district_list`
In theory with() only works with relationships, so do I have any options for associating the district_list fields to my Child models?
EDIT: Actually, I was wondering whether with('educationProfiles') generates SQL equivalent to:
EducationProfile::whereIn('id',array(1,2,3,4))
or whether it's actually equivalent to
DB::table('education_profiles')->whereIn('id',array(1,2,3,4))
The reason I ask is that in the former I'm getting models, if it's the latter I'm getting unmodeled data, and thus I can probably mess it up as much as I want. I assume with() generates an additional set models, though. Anybody care to correct or confirm?
Ok, I think I've cracked this nut. No, it is NOT possible to eager load arbitrary queries. However, the tools have been provided by the Fluent query builder to make it relatively easy to replicate eager loading manually.
First, we leverage the original query:
$query = Child::where('date_of_birth','>','2004-03-27')->with('educationProfiles');
$children = $query->get();
$eagerIds = $query->lists('id');
Next, use the $eagerIds to filterDB::table('education_profile') in the same way that with('educationProfiles') would filter EducationProfile::...
$query2 = DB::table('education_profile')->whereIn('child_id',$eagerIds)->select('child_id', 'GROUP_CONCAT(`education_profiles`.`district`) as `district_list`')->groupBy('child_id');
$educationProfiles = $query2->lists('district_list','child_id');
Now we can iterate through $children and just look up the $educationProfiles[$children->id] values for each entry.
Ok, yes, it's an obvious construction, but I haven't seen it laid out explicitly anywhere before as a means of eager loading arbitrary calculations.
You can add a where clause to your hasMany() call like this:
public function educationProfilesUnderTen() {
$ten_years_ago = (new DateTime('10 years ago'))->format('Y-m-d');
return $this->hasMany('EducationProfile')->where('date_of_birth', '>', $ten_years_ago)
}
Here is a very basic example of what I want to do. The code I have come up with seems quite verbose... ie looping through the collection, etc.
I am using a Telerik MVC grid that posts back a collection of deleted, inserted and updated ViewModels. The view models are similar but not exactly the same as the entity.
For example... I have:
Order.Lines. Lines is an entity collection (navigation property) containing OrderDetail records. In the update action of my controller using the I have a List names DeletedLines pulled from the POST data. I also have queried the database and have the Order entity including the Lines collection.
Now I basically want to tell it to delete all the OrderDetails in the Lines EntityCollection.
The way I have done it is something like:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.Where(l => l.Key == line.Key).SingleOrDefault())
}
I was hoping there was a way that I could use .Interset() to get a collection of entities to delete and pass that to DeleteObject.. however, DeleteObject seems to only accept a single entity rather than a collection.
Perhaps the above is good enough.. but it seemed like there should be an easier method.
Thanks,
BOb
Are the items in DeletedLines attached to the context? If so, what about this?
foreach (var line in DeletedLines) db.DeleteObject(line);
Response to comment #1
Ok, I see now. You can make your code a bit shorter, but not much:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.SingleOrDefault(l => l.Key == line.Key))
}
I'm not sure if DeleteObject will throw an exception when you pass it null. If it does, you may be even better off using Single, as long as you're sure the item is in there:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.Single(l => l.Key == line.Key))
}
If you don't want to re-query the database and either already have the mapping table PK values (or can include them in the client call), you could use one of Alex James's tips for deleting without first retrieving:
http://blogs.msdn.com/b/alexj/archive/2009/03/27/tip-9-deleting-an-object-without-retrieving-it.aspx