How to get parent product id in magento? - php

I know that in Magento 1.4.2.0 one gets parent id's like so
list( $parentId ) = Mage::getModel('catalog/product_type_configurable')
->getParentIdsByChild( $product->getId() );
My question is: if I don't know what the parent is, how do I know to use the 'catalog/product_type_configurable' vs 'catalog/product_type_grouped' model to get the id?

You can just call both and offer a fall-back as it should be one or the other:
if($product->getTypeId() == "simple"){
$parentIds = Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($product->getId());
if(!$parentIds)
$parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
if(isset($parentIds[0])){
$parent = Mage::getModel('catalog/product')->load($parentIds[0]);
// do stuff here
}
}

You may use:
$product->getTypeInstance();
Which will return the type object of your product
Then you can perform your:
->getParentIdsByChild()
Giving finally:
$product->getTypeInstance()->getParentIdsByChild($child->getId());

Here is another solution for magento 1.7.2
$parentIds = Mage::getSingleton('catalog/product_type_configurable')->getParentIdsByChild($mageProduct->getId());

we can use in block file,magento 2,
protected $_catalogProductTypeConfigurable;
public function __construct(
\Magento\Catalog\Block\Product\Context $context,
//for getting parent id of simple
\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable,
array $data = []
) {
//for getting parent id of simple
$this->_catalogProductTypeConfigurable = $catalogProductTypeConfigurable;
parent::__construct($context, $data);
}
public function getProductData($id){
$parentByChild = $this->_catalogProductTypeConfigurable->getParentIdsByChild($id);
if(isset($parentByChild[0])){
//set id as parent product id...
$id = $parentByChild[0];
}
return $id;
}

You could check the type of the product with $_product->getTypeId(); and if this returns 'configurable', take the configurable model and if it returns 'grouped' take the grouped model.
Hope this helps.

Related

Create layered navigation with custom productcollection magento2

I have a block class which has:
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Magento\Catalog\Helper\Category $categoryHelper,
\Magento\Catalog\Model\Indexer\Category\Flat\State $categoryFlatState,
\Magento\Catalog\Model\CategoryFactory $categoryFactory,
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
....
) {
$this->_categoryHelper = $categoryHelper;
$this->_categoryFactory = $categoryFactory;
$this->_collectionFactory = $collectionFactory;
$this->categoryFlatConfig = $categoryFlatState;
$this->_productCollectionFactory = $productCollectionFactory;
parent::__construct($context);
....
}
and a function of...
public function getProductCollection($childId)
{
$categoryId = $childId;
$category = $this->_categoryFactory->create()->load($categoryId);
$collection = $this->_productCollectionFactory->create();
$collection->addAttributeToSelect('name');
$collection->addAttributeToSelect('url');
$collection->addAttributeToSelect('image');
$collection->addAttributeToSelect('price');
$collection->addAttributeToSelect('special_price');
$collection->addCategoryFilter($category);
$collection->addAttributeToFilter('visibility', \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH);
$collection->addAttributeToFilter('status',\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED);
return $collection;
}
How do I use this production collection to create a layered navigation which uses attributes available for the products returned, and then be able to filter through with those.
Due to the nature of the site, I have to loop through this X amount of times on a category page, as I am getting products of each subcategory on this page in a certain way due to the design.
So in my template for E.G I have:
`$products= $this->getProductCollection($idhere);
foreach ($getmeprods as $products):?>
... looping through data
<?php endforeach;?>
`
Any help would be appreciate but I am rather boggled here!

Problems with Doctrine and Where

I'm having problems with this Query. I want to obtain only the cars with the atribute $categoria and I do this:
public function listcategoriaAction($categoria)
{
$posts = $this->get('doctrine')->getManager()->createQueryBuilder()->select('p')->from('CarsCarsBundle:Post', 'p')->where('p.categoria = :categoria')->setParameter('categoria', $categoria)->getQuery()->getResult();
return $this->render('CarsCarsBundle:Cars:list.html.twig', array('posts' => $posts));
}
But what I recieve is an empty array. Any tips will be appreciated
First of all, I assume that this code is in the controller. I strongly recommend you to avoid putting queries on your controller, instead use repositories.
I think this error happened because you didn't hydrate previously the category id you received by parameter. This would do the trick:
$dm = $this->get('doctrine')->getManager();
//This gets the object from db
$category = $dm->getRepository('CarsCarsBundle:Category')->findOneById($categoria);
if ($category !== null) {
$posts = $dm->getRepository('CarsCarsBundle:Post')->findOneByCategory($category);
} else {
//The id received is not on the db.
}

Magento 2 How to get category by url_key

I try to get a category in Magento 2.0 by it url_key.
Now I've got :
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$categoryFactory = $objectManager->create('Magento\Catalog\Model\CategoryFactory');
$category = $categoryFactory->create()
->addAttributeToFilter('url_key','my_category_url_key');
It returns me this error :
Error filtering template: Invalid method
Magento\Catalog\Model\Category\Interceptor::addAttributeToFilter(Array
( [0] => url_key [1] => my_category_url_key ) )
Thanks.
/**
* #var \Magento\Catalog\Model\CategoryFactory
****** inject in constructor ******
*/
protected $categoryFactory;
---------
---------
---------
$categories = $this->categoryFactory->create()
->getCollection()
->addAttributeToFilter('url_key','devops')
->addAttributeToSelect(['entity_id']);
echo "<pre>";
print_r($categories->getFirstItem()->getEntityId());
I know this is an old question, but in case anybody wonders...
All answers here use the ObjectManager. That's bad practice. The correct way to implement this, is as follows:
namespace Vendor\Module\Model;
use Magento\Catalog\Model\CategoryFactory;
class MyClass {
private $categoryFactory;
public function __construct(
CategoryFactory $categoryFactory
} {
$this->categoryFactory = $categoryFactory;
}
public function MyFunction() {
$categoryFactory = $this->categoryFactory->create();
$category = $categoryFactory->loadByAttribute('url_key', 'my_category_key');
$categoryId = $category->getId(); // E.g. if you want the ID.
}
In this example, category will contain the object of the category with URL-key 'my_category_key'.
addAttributeToFilter is a method of collections.
You should execute in on a category collection, not on a category instance.
Try this below code, i hope you will get your result.
<?php
$objectManagerr = \Magento\Framework\App\ObjectManager::getInstance();
$categoryFactory = $objectManagerr->create('Magento\Catalog\Model\ResourceModel\Category\CollectionFactory');
$categoryy = $categoryFactory->create()
->addAttributeToFilter('url_key','your_category_url_key')
->addAttributeToSelect('*');
foreach ($categoryy as $productt){
echo $productt->getName().'<br>';
echo $productt->getId();
}
?>
Based on your code, you missed the correct method in order to get a category via url_key.
Right now we can use method loadByAttribute, so you code should be something like this:
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$categoryFactory = $objectManager->create('Magento\Catalog\Model\CategoryFactory');
$category = $categoryFactory->create()->loadByAttribute('url_key','my_category_url_key');

DataMapper ORM - How to get all of a model and each instance's related data

I do not understand how to do the following:
Lets say I have a product table, and a photo table. 1 Product has many photos. So in the product model I do:
var $has_many = array("category", "photo");
Now I want to get all products and relate each of their photos to them. How can I do this? Currently, in my controller I am going through each of the products and querying photos and passing a separate array that way. This CANNOT be ideal. I should be able to tie each photo to the specific product directly no?
Logically, this would work (but it doesnt?)
$product = new Product;
$products = $product->get_by_related_category('name', $where);
$photos = $product->photo->get();
See what I'm getting at? I would love to just pass that $products variable to my view, be able to foreach through it, and have an array of photos tied to each product object.
How can I accomplish this? Or is there a better way to do this?
Thanks!
With a "has many" relation you basically have two way to fetch the related information with SQL:
You can join the other table in like select products.*, photos.* from products left outer join photos on products.id = photos.product_id. This way you will have "duplicate" products data so you need to handle the results accordingly. Unfortunately include_related() doesn't support this directly, it would create the duplicated products with each of them have one related photo in your case.
You can run two queries, first fetching the products (select * from products where ...) and then fetching the photos with the id's of the selected products (select * from photos where product_id in (...)) and sort out what photos row should go what product. There's no built-in functionality for this in DMZ, but here's what I've coded up for a model base class (that extends the DataMapper class) that can be used like this:
$products = new Product;
$products = $products
->get_by_related_category('name', $where) // first get the parent objects
->load_related('photo'); // then load in the related ones inside them
foreach ($products as $product) {
// unique product instances as before
foreach ($product->photo as $photo) {
// and every product has a list of related photos
// for each product individualy
}
}
The method below will gather the id's of the parent objects, run one SQL query with the ids in a where_in() and sort the results out for the parent object's related field object (unfortunately its a little long and doesn't support many-to-many relations).
/**
* load_related
*
* Loads related has_many objects efficiently
*
* #param string $related_field_name the name of the relation
* #param callable $filter_function callback to place extra conditions on the related model query
*/
public function load_related($related_field_name, $filter_function = null) {
$related_properties = $this->_get_related_properties($related_field_name);
$related_models = new $related_properties['class'];
$join_field = $related_properties['join_self_as'].'_id';
$ids = array_unique(array_filter(array_merge(array('id' => $this->id), array_column($this->all, 'id')), 'intval'));
if (empty($ids)) {
return $this;
}
$related_models->where_in($join_field, $ids);
if (is_callable($filter_function)) {
call_user_func($filter_function, $related_models);
}
$related_models = $related_models->get();
$related_models = array_group_by($related_models, $join_field);
foreach ($this->all as $i => $row) {
$related_models_to_row = isset($related_models[$row->id]) ? $related_models[$row->id] : null;
if ($related_models_to_row) {
$this->all[$i]->{$related_field_name} = reset($related_models_to_row);
$this->all[$i]->{$related_field_name}->all = $related_models_to_row;
}
}
if (isset($related_models[$this->id])) {
$this->{$related_field_name} = $related_models[$this->id];
}
return $this;
}
// The two array helper functions used above from my_array_helper.php
function array_group_by($arr, $key, $index_by_col = false) {
$re = array();
foreach ($arr as $v) {
if (!isset($re[$v[$key]])) {
$re[$v[$key]] = array();
}
if ($index_by_col === false) {
$re[$v[$key]][] = $v;
} else {
$re[$v[$key]][$v[$index_by_col]] = $v;
}
}
return $re;
}
function array_column($arr, $key, $assoc = false) {
if (empty($arr)) {
return array();
}
$tmp = array();
foreach ($arr as $k => $v) {
if ($assoc === true) {
$tmp[$k] = $v[$key];
} elseif (is_string($assoc)) {
$tmp[$v[$assoc]] = $v[$key];
} else {
$tmp[] = $v[$key];
}
}
return $tmp;
}
I'm kinda exploring DM for a while now and I needed the same functionality. At first the load_related function from the other answer seemed to be the solution for this.
I did some further research though. I found this answer to another question and it made me thinking if there wasn't a way to autopopulate only some of the relations.
Well, there is !!
You can set this 'option' if you make a relation in a model.
//Instead of doing this:
var $has_many = array('user_picture');
//Do this
var $has_many = array(
'user_picture' => array(
'auto_populate' => TRUE,
),
);
Now the pictures will be available in a user object.
foreach ($u as $user) {
foreach ($user->user_picture as $picture) {
// Do your thing with the pictures
}
}
I found it on this page from the docs.
Enjoy!

Which is the event listener after doSave() in Symfony?

I've been looking at this event-listeners page http://www.doctrine-project.org/documentation/manual/1_1/pl/event-listeners and I'm not sure which is the listener I have to use to make a change after the doSave() method in the BaseModelForm.class.php.
// PlaceForm.class.php
protected function doSave ( $con = null )
{
...
parent::doSave($con);
....
// Only for new forms, insert place into the tree
if($this->object->level == null){
$parent = Place::getPlace($this->getValue('parent'), Language::getLang());
...
$node = $this->object->getNode();
$method = ($node->isValidNode() ? 'move' : 'insert') . 'AsFirstChildOf';
$node->$method($parent); //calls $this->object->save internally
}
return;
}
What I want to do is to make a custom slug with the ancestors' name of that new place. So if I inserting "San Francisco", the slug would be "usa-california-san-francisco"
public function postXXXXXX($event)
{
...
$event->getInvoker()->slug = $slug;
}
The problem is that I'm inserting a new object with no reference to its parent. After it's saved, I insert it to the tree. So I can't change the slug until then.
I think a Transaction listener could work, but I'm use there is a better way I'm not seeing right now.
thanks!
You are looking at the wrong piece of code. As stated by benlumley, you should manage your slug directly in the model, not in the form. To achieve what you want (a recursive slug) is quite easy using doctrine's Sluggable behavior. You need to implement a getUniqueSlug() into your model so that it gets called by the behavior (it's automatic) and handle your slug specifities in there:
public function getUniqueSlug()
{
$slug = '';
$parent = $this->getParent();
if ($parent->exists())
{
$slug = $this->getParent()->getUniqueSlug().'-';
}
return $slug.$this->getName();
}
What we do here is basically traverse all the ancestors of the current object and append the slugs on the go (replace the getParent() by whatever method you use to retrieve an object's parent.
Firstly, I'd put this into the model rather than the form - that way if the object is ever edited/updated the behaviour would still happen.
In the form though, I'd use updateObject:
function updateObject($values = array()) {
parent::updateObject($values);
// do your stuff
}
In the model (looks like you are using doctrine ...) I'd put this into the postSave() method. As I say, I think its better there than the form.
I had the same problems, and the Doctrine_Record::postInsert(Doctrine_Event $event) method did not work for me. Indeed the node aren't hydrated yet.
I had to overwrite the sfFormObject::doSave method like this:
protected function doSave($con = null)
{
$is_new = $this->isNew();
parent::doSave($con);
$this->doSaveNestedSet($con);
$service = $this->getObject();
if( $is_new and ! $service->getClientId() and $parent = $service->getParent())
{
$service->setClient($parent->getClient());
$service->save();
}
}

Categories