Magento: how to merge two product collections into one? - collections

if i have two product collections is there a way to merge them into one?
for example (my final intent isn't to actually just get a collection of 2 cats, this is just to illustrate the issue):
$collection1 = Mage::getModel('catalog/category')->load(148)->getProductCollection();
$collection2 = Mage::getModel('catalog/category')->load(149)->getProductCollection();
$merged_collection = merge_collections($collection1,$collection2);
any help would be appreciated!

Assuming the items you wish to group together are of the same type and exist in the database then you can do this:
$collection1 = Mage::getModel('catalog/category')->load(148)->getProductCollection();
$collection2 = Mage::getModel('catalog/category')->load(149)->getProductCollection();
$merged_ids = array_merge($collection1->getAllIds(), $collection2->getAllIds());
// can sometimes use "getLoadedIds()" as well
$merged_collection = Mage::getResourceModel('catalog/product_collection')
->addFieldToFilter('entity_id', array('in' => $merged_ids))
->addAttributeToSelect('*');
Here I know to filter by entity_id because that is products' key field, like it is for most entity types, some flat tables have a different primary key. Often you can generalise that with one of the collection's getIdFieldName() method. Products are a bad example in this case because it's ID field name isn't filled out correctly.

Almost every (or every?) collection in Magento inherits from a Varien Data Collection. A collection is a special object that holds objects of another type. There's no method for merging collections, but you can add additional items of the appropriate type to the collection.
Code like this should get you where you want to go, although there's probably more efficient ways to loop and do the actual merging.
$collection1 = Mage::getModel('catalog/category')->load(148)->getProductCollection();
$collection2 = Mage::getModel('catalog/category')->load(149)->getProductCollection();
//load an empty collection (filter-less collections will auto-lazy-load everything)
$merged = Mage::getModel('catalog/product')->getCollection()->addFieldToFilter('entity_id',-1);
//add items from the first collection
foreach($collection1 as $item)
{
$merged->addItem($item);
}
//add items from the second collection
foreach($collection2 as $item)
{
//magento won't let you add two of the same thing to a collection
//so make sure the item doesn't already exist
if(!$merged->getItemById($item->getId()))
{
$merged->addItem($item);
}
}
//lets make sure we got something
foreach($merged as $product)
{
var_dump($product->getName());
}

I don't think there is such a method, but you can probably do something like that :
foreach ($collection1 as $item){
$collection2->addElem($item);
}

you can filter your collection directly without using 2.
$products = Mage::getModel('catalog/product');
$_collection = $products->getCollection();
->addCategoryFilter(2)
->load();
or try using 'addItem' to add your results to a collection. See also in Magento wiki

for the collection of products of several categories, you can use the following code
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('name')
->addAttributeToSelect('sku');
$collection->getSelect()
->join(
'catalog_category_product',
'product_id=entity_id',
array('category_id')
)
->where('catalog_category_product.category_id IN (?)', $categories);

Related

Drupal entityQuery multiple node types and join

Looking to join multiple nodes via a query and join. Let's say one node has a matching ID of another field in another node. I would like to join them into an array so that I can output other fields with them indexed together on the same ID. Example: node1.nid and node2.field.target_id
How do I do this?
$nids = \Drupal::entityQuery('node')->accessCheck(FALSE)
->join('node2', 'n2', 'n.nid = n2.field.target_id');
->condition('type', 'node1', 'node2')
->sort('title')->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
$response = array();
This is definitely doable and it looks like you're almost there, but it looks like you're combining the Database API with the Entity API, specifically trying to run Database joins on EntityQuery fetches.
Now, if you want to continue the Database query route, which may make it a little easier to join the values of multiple entities and output them, this is what I would recommend:
The first thing that I notice is that you're attempting to chain the join. Unfortunately, according to the Database API Docs
Joins cannot be chained, so they have to be called separately (see Chaining).
I have done similar to this with a join between two custom content types. Here is the code I used:
$connection = Database::getConnection();
if ($connection->schema()->tableExists('companies') && $connection->schema()->tableExists('users')) {
$query = $connection->select('companies', 'c');
$query->join('users', 'u', 'c.sourceid1 = u.sourceid1');
$results = $query->fields('u', ['destid1', 'sourceid1'])
->fields('c', ['destid1'])
->execute()
->fetchAll();
return $results;
}
Now this assumes that you've brought in Drupal\Core\Database\Database, but let's walk through the process.
$connection = Database::getConnection();
This is fetching the database connection for your site
if ($connection->schema()->tableExists('companies') && $connection->schema()->tableExists('users')) {
This is a check I always do to make sure the tables I'm working with exist.
$query = $connection->select('companies', 'c');
One of your tables needs to be the "base" table. In this case, I chose companies.
$query->join('users', 'u', 'c.sourceid1 = u.sourceid1');
This line is where the join actually happens. Notice that it's not chained, but it's own command. We're joining these tables, users and companies, on this sourceid1.
$results = $query->fields('u', ['destid1', 'sourceid1'])
->fields('c', ['destid1'])
->execute()
->fetchAll();
This is where I'm pulling whichever fields I want from both entities. In this case I want destid1 and sourceid1 from my user table and destid1 from my company table.
The ->execute() call actually runs the query, and ->fetchAll() returns an array of all matching results. This will get you to being able to JSONify all the things.
However, if you want to stick with the entityQuery route, that's also viable. My personal preference is to use a regular query for more complex things and an entityQuery if I just need to get a value from a single field or return a single entity.
For an entityQuery, well, that's a bit different. I wouldn't really recommend using an entityQuery for a join here. Instead, using entityTypeManager if you know or can get the matching value from both tables.
For example, let's assume that field_content_type_a_id on content type A matches field_related_type_a_id on content type B.
You could load one, let's go with type A for now:
// Get the current NID somehow... that's up to you
$entity_a = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
// Get the value of the field you want to match to the other content type
$matching_value = $entity_a->get('field_content_type_a_id')->getValue();
// Get the entities with matching type A values
$entity_b_array = \Drupal::entityTypeManager()->getStorage('node')
->loadByProperties(['field_related_type_a_id' => $matching_value]);
// Do something with $entity_b_array.

Displaying field collections loop in Content Type TPL file for drupal 7

I have a content type "about" created in Drupal 7. I have a field collection named "field_usf_projects" which is set to unlimited and contains 2 fields, "usf_title" and "usf_description". Now I want to run a for loop which retrieves the field_usf_projects and then displays 2 fields namely ("usf_title" and "usf_description") inside a ul - li structure.
I have gone through many links but cannot find a working solution. Please help me with this.
Thanks in advance.
Here is my solution, on hook_node_view you can use the entity wrapper to get the fields
function mymodule_node_view($node, $view_mode, $langcode) {
// Check if the node is type 'about'.
if ($node->type != 'about') {
return;
}
// Get the contents of the node using entity wrapper.
$node_wrapper = entity_metadata_wrapper('node', $node);
// Get the contents of the field collection.
$values = $node_wrapper->field_usf_projects;
// Loop field_usf_projects.
foreach ($values as $item) {
// Print the values of the fields.
var_dump($item->usf_title->value());
var_dump($item->usf_description->value());
}
}
Instead of dumping, you can add the markup for your
A nicer thing to do would be use the hook_preprocess_node to add the markup straight into the $variables, and print them via template.
https://api.drupal.org/api/drupal/modules!node!node.module/function/template_preprocess_node/7
I can tell you how I'm handling this, event it's a bit dirty and I'm risking to be crucified, but it works for me. If you are inside node template you have $node object. Print it with print_r or similar way and just follow the structure of output to get to your data. It will probably be something like:
$node->field_usf_title['und']...
If you don't have that $node object find node id and load the node with
$node = node_load($nid);
first.
I was finally fed up with Field collection. I cannot get any data. I have used Multifield which is way too much better than Field collection. Please see it at https://www.drupal.org/project/multifield
Let me know what is better multi field or Field Collection.
Thanks!

Create new object from existent object in DB using Symfony2 and Doctrine2

I need to create a new set of DB records by using same data that already exists. See image below:
Using this as example I need to create the same set of records but just changing the column company, less take UPC = I5TYF1UPORMYUY4JT21Z, using this info I should be able to generate two rows by just changing the company to 52 (any number). What I think is to get those records using this:
$entityStockDetailHasProductDetail = $this->getDoctrine()->getManager()->getRepository('ProductBundle:StockDetailHasProductDetail')->findBy(array(
"product" => $request->request->get('product')['id'],
"upc" => $request->request->get('product')['upc'],
));
Which returns the right records but I don't know how to continue from that point. My entity has all this methods:
$entityStockDetailHasProductDetail->setProductDetail();
$entityStockDetailHasProductDetail->setUpc();
$entityStockDetailHasProductDetail->setProduct();
$entityStockDetailHasProductDetail->setCompany();
$entityStockDetailHasProductDetail->setCondition();
$entityStockDetailHasProductDetail->setContent();
Any ideas?
Just loop over the collection, clone your entities, set the new company and persist.
$em = $this->getDoctrine()->getEntityManager('default');
$collection = $em->getRepository('YourBundle:Entity')->findBy(array('...'));
foreach ($collection as $entity) {
$newEntity = clone $entity;
$newEntity
->setId(null)
->setCompany($company)
;
$em->persist($newEntity);
}
$em->flush();

Symfony2 Select one column in doctrine

I'm trying to refine the query trying to select fewer possible values ​​..
For example I have an entity "Anagrafic" that contains your name, address, city, etc.,
and a form where I want to change only one of these fields, such as address.
I have created this query:
//AnagraficRepository
public function findAddress($Id)
{
$qb = $this->createQueryBuilder('r')
->select('r.address')
->where('r.id = :id')
->setParameter('id', $Id)
->getQuery();
return $qb->getResult();
}
there is something wrong with this query because I do not return any value, but if I do the query normally:
//Controller
$entity = $em->getRepository('MyBusinessBundle:Anagrafic')->find($id);
Return the right value.
How do I do a query selecting only one column?
Since you are requesting single column of each record you are bound to expect an array. That being said you should replace getResult with getArrayResult() because you can't enforce object hydration:
$data = $qb->getArrayResult();
Now, you have structure:
$data[0]['address']
$data[1]['address']
....
Hope this helps.
As for the discussion about performance in comments I generally agree with you for not wanting all 30 column fetch every time. However, in that case, you should consider writing named queries in order to minimize impact if you database ever gets altered.
You can use partial objects to only hydrate one field and still return a object.
This worked for me:
$qb = $repository->createQueryBuilder('i')
->select('i.name')
->...
Use partial objects like this to select fields
$qb = $this->createQueryBuilder('r')
->select(array('partial r.{id,address}'))
...
Put your field names between the brackets

Symfony2, Doctrine, Updation OneToMany entries

i have function
private function updateCharacters($oApiKeyInfo) // argument is base entity
{
$aXmlCharacter = $this->fXml->getXmlRowset($this->oXml, 'row');
// insert new
foreach ($aXmlCharacter as $oXmlCharacter)
{
$loopData = $this->fXml->getXmlAttributesAsArray($oXmlCharacter);
$oApiKeyInfoCharacters = new apiKeyInfoCharacters();
$oApiKeyInfoCharacters
->setKeyID($this->keyID)
->setCharacterID($loopData['characterID'])
->setCharacterName($loopData['characterName'])
->setCorporationID($loopData['corporationID'])
->setCorporationName($loopData['corporationName'])
->set_apiKeyInfo_byKeyID($oApiKeyInfo);
$this->em->persist($oApiKeyInfoCharacters);
}
// $this->em->flush() is in main (public) function
}
but, it creates dublicates... and i want that in db was ONLY that entries that is in $aXmlCharacter (array), others must be deleted
now code above is only adding new entries, but i need remove previous
can someone help to deal with it? and please show working examples
Dublicate entries i can not see any definition for uniquenes.
Deleting an Object vom database is simple as the documentation shows.
$product = $em->getRepository('YourBundle::apiKeyInfoCharacters')->find($id);
$em->remove($product);
$em->flush();
But why do you want to delete the existing one instead of updating?

Resources