TYPO3 extbase - ObjectStorage and 1:n relation - php

I have an Item object which has a 1:n relation to categories. My Item Model contains:
setCategories(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $categories)
getCategories()
addCategory(VENDX\Items\Domain\Model\Category $category)
removeCategory(VENDX\Items\Domain\Model\Category $category)
but I am not able to add multiple categories to an itemobject.
i tried:
$category = $this->objectManager->get('VENDX\Items\Domain\Model\Category');
$category->setCatName('Cat1'); //First category
$item->addCatgeory($category);
$category->setCatName('Cat2'); //Second category
$item->addCategory($category);
after adding $item to my $itemrepository it just saves the last category "Cat2" into the db. What am i missing??
also tried that:
$categories = $this->objectManager->get('TYPO3\CMS\Extbase\Persistence\ObjectStorage');
$category = $this->objectManager->get('VENDX\Items\Domain\Model\Category');
$category->setCatName('Cat1'); //First category
$categories->attach($category);
$category->setCatName('Cat2'); //Second category
$categories->attach($category);
$item->setCategories($categories);
same issue with the above code. It just saves the last (second) category. How can i add multiple categories to my item-object?

Well i made a fatal error when using the SAME category-object. In fact i just changed its CatName value. In ORM we need one object for each "value". Means we can't use the same object for multiple "object-allocations" like i did above. So the correct way of achieving my purpose is:
$categories = $this->objectManager->get('TYPO3\CMS\Extbase\Persistence\ObjectStorage');
$category1 = $this->objectManager->get('VENDX\Items\Domain\Model\Category'); //1st catobj
$category1->setCatName('Cat1'); //First category
$categories->attach($category1);
$category2 = $this->objectManager->get('VENDX\Items\Domain\Model\Category'); //2nd catobj
$category2->setCatName('Cat2'); //Second category
$categories->attach($category2);
$item->setCategories($categories);
another "mistake" was using the objectManager for entities-instantiation. I was told to construct them via "new" instead of "overheading" the extension with the objectManager.
so my final solution is:
$categories = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage;
$category1 = new \VENDX\Items\Domain\Model\Category; //1st catobj
$category1->setCatName('Cat1'); //First category
$categories->attach($category1);
$category2 = new \VENDX\Items\Domain\Model\Category; //2nd catobj
$category2->setCatName('Cat2'); //Second category
$categories->attach($category2);
$item->setCategories($categories);

Related

Attach result of for loop to eloquent output in Laravel

Good day. Like I explained in this post Using Query Builder and MySql to get categories and sub - categories, I want to make something meaningful from this table. I decided to go the Eloquent way. I am able to get the distinct categories as shown below. How do i loop through each title belonging to a a particular category?
For example, get it in the form:
CatA
Title 1
Title 5
CatB
Title 2
Title 3
Title 4
My code is shown below
public function type(Request $request){
$details = [];
$categories = Category::distinct()->get(['category']); //Get the distinct categories from the category column in the table
foreach($categories as $category){
$titles = Category::where('type',$category->type);
$details = ['cat'=>$category->type,[
'title' => $titles->title,
'pk' => $titles->pk
]];
}
return response()->json([
"Data" => $details
]);
}
Not getting a result. Is there a better way this can be done please?
You can make many to many relationship with post and category. Then get all categories with posts.
Example:
$categories = Category::with('posts')->all();
Then you can loop through categories and posts
foreach($categories as $category){
foreach($category->posts as $post){
echo $post->title;
}
}
Documentation: https://laravel.com/docs/8.x/eloquent-relationships#many-to-many

Laravel 5.2 Eloquent foreach loop

I'm in a controller for Laravel 5.2 and am trying to iterate through an eloquent collection of invoice_items, which would translate to something like order items. So, the invoice would act as the order, have it's ordered items (invoice_item), and the invoice_items would list all of the products ordered (product).
Here's what I have:
$id = $value; //from param
$invoice = Invoice::where('id', $id)->get();
$invoice_items = Invoice_item::all()->where('invoice_id', $invoice[0]->id);
$contact = Contact::where('id', $invoice[0]->contact_id)->get();
foreach($invoice_items as $item) {
$products = Product::all()->where('id', $item->product_id);
}
I'm attempting to pull all of the products from that specific invoice (via invoice items), which in this specific case should be two, different products.
What's happening, is when I iterate through using that loop, it's adding the same product twice, whereas it should be adding each product once. Is my logic just wrong here? Or do I need to look at my relationships again or something?
Change your queries to:
$invoice = Invoice::where('id', $id)->get();
$invoice_items = Invoice_item::where('invoice_id', $invoice[0]->id)->get();
$contact = Contact::where('id', $invoice[0]->contact_id)->get();
foreach($invoice_items as $item) {
$products = Product::where('id', $item->product_id)->get();
}
An easier way may be to add a items relation to the InvoiceItems model. E.g.:
public function items()
{
$this->hasOne('Items');
}
Then you can get all the Items from Invoice_item using:
return $invoice_items->items;
You can also try:
$invoice = Invoice::where('id', $id)->get();
$invoice_items = Invoice_item::where('invoice_id', $invoice[0]->id)->get()->lists('product_id');
$contact = Contact::where('id', $invoice[0]->contact_id)->get();
$products = Product::whereIn('id', $invoice_items)->get();
Hopefully, $products will then contain a collection of products for that invoice. No need for a foreach loop.

Symfony2, PHP decrease query weight when counting many objects from database

So I have 4 tables in my database. Product, Category, ProductCategory and ProductSubcategory.
These tables are in a ManyToMany relationship.
All products are saved in table: Product.
All Categories and Subcategories are saved in one table: Category.
A Product can have a Subcategory without the Category, can have both, or can have a Category without any Subcategory.
ProductCategory and ProductSubcategory is holding all the relationships.(product_id and category_id/subcategory_id)
Now in my twig I want to count how many products does the category and subcategory hold. For that I am using Twig Extensions.
For Subcategory:
Counting how many products are in a subcategory is easy and it works.
In Twig:
{% for subcategory in categories.children %}
[{{ getProductsBySubcategory(subcategory.id) }}]
{% endfor %}
The function:
public function getProductsBySubcategory($id)
{
$products = 0;
$subcategories = $this->doctrine->getRepository('MpShopBundle:ProductSubcategory')->findBy(array('subcategory' => $id));
foreach ($subcategories as $subcategory) {
$products++;
}
return $products;
}
For Category:
For category its a little bit different. Here I want to count all the products in the Category and all the products in Subcategories that this category has. First I check if the category has products and if it has I add the to the array. Then I check if this category has subcategories. If it does i get all the products from the subcategories and add them in to the same array. Now if the product is in both the category and subcategory i dont want to double count. Thats why I am storing everything in an array(to do array_unique later).
My Twig:
[{{ getProductsByCategory(categories.id)|length }}]
The function:
public function getProductsByCategory($id)
{
$category = $this->doctrine->getRepository('ApplicationSonataClassificationBundle:Category')->find($id);
$productCategories = $this->doctrine->getRepository('MpShopBundle:ProductCategory')->findBy(array('category' => $id ));
$productSubcategories = $this->doctrine->getRepository('MpShopBundle:ProductSubcategory')->findAll();
$products = array();
foreach($productCategories as $productCategory) {
array_push($products, $productCategory->getProduct()->getId());
}
foreach($productSubcategories as $productSubcategory) {
$subcategory = $productSubcategory->getSubcategory();
if($subcategory->getparent() == $category) {
array_push($products, $productSubcategory->getProduct()->getId());
}
}
$result = array_unique($products);
return $result;
}
THE PROBLEM:
Everything works in local. I am getting the correct number. However when I switch to Live I get error:500. I am thinking that this is probably because Live has thousands of products and allot of categories. So the foreach loops are breaking the server. Or maybe its because i am saving everything in array?
What should I do? Is it the server problem and I need to increase something? Or maybe there is a way to count without using arrays and decreasing the loop count? But how to check for duplicates then?
The logs show this:
Allowed memory size of 536870912 bytes exhausted (tried to allocate 77 bytes) in /var/www/kd_cms/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php on line 40
Dql is faster than foreach with large db objects
// .../ProductCatRepository.php
public function findDistinctProductsByCat($catId)
{
$query = $this->createQueryBuilder('entity')
->leftjoin('entity.category', 'cat')
->andWhere('cat.id = :cat')
->setParameter('cat', $catId)
;
return $query->getQuery()->getResult();
}
// .../ProductSubCatRepository.php
public function findDistinctProductsByCatSubCat($catId)
{
$query = $this->createQueryBuilder('entity')
->leftjoin('entity.subcategory', 'subcat')
// maybe missing a leftJoin with cat idk your mapping
// ->leftJoin('subcat.category', 'cat')
// ->andWhere('cat.parent = :cat')
->andWhere('subcat.parent = :cat')
->setParameter('cat', $catId)
;
return $query->getQuery()->getResult();
}
public function getProductsByCategory($id)
{
$categoryId = $id;
$productsCat = $this->doctrine->getRepository('MpShopBundle:ProductCat')->findDistinctProductsByCat($categoryId);
$productsSubCat = $this->doctrine->getRepository('MpShopBundle:ProductSubCat')->findDistinctProductsByCatSubCat($categoryId);
// ArrayCollection::count()
return array('count_cat' => $productsCat->count(), 'product_subcat' => $productsSubCat->count(), 'result' => array_unique(array_merge($productsSubCat, $productsCat)));
}

Get manufacturers of all products in a category from non-product page

I made the following method in custom Magento controller to retrieve all the manufacturers in the specified category. The module is made as a service to get the data for ajax calls.
I made a number of methods like this and all are executed on my local server in the range of 5-7 seconds. This one takes 14 seconds to execute on local server.
Can you help me to find a bottleneck here:
public function subcategoryAction() {
$storeId = Mage::app()->getStore()->getStoreId();
// Subcategory ID passed with a GET method
$sub = $this->getRequest()->getParam('subcategory');
if ($sub) {
// Querying to get all product ID's in the specified subcategory
$product_ids = Mage::getResourceModel('catalog/product_collection')
->setStoreId($storeId)
->addAttributeToFilter('status', array('eq' => '1'))
->addAttributeToFilter('visibility', 4)
->addCategoryFilter(Mage::getModel('catalog/category')
->load($sub))->getAllIds();
$product = Mage::getModel('catalog/product');
// Load all the product models by their ID's
foreach ($product_ids as $id) {
$product->load($id);
$manufacturers[] = $product->getAttributeText('manufacturer');
}
// Getting unique values of manufacurers, just like array_unique
$manufacturers[$product->getAttributeText('manufacturer')] = $product->getAttributeText('manufacturer');
// Echoing default option value
echo "<option value='all'>BRAND/MAKE</option>";
// Echoing and formatting manufacturers for a dropdown
foreach ($manufacturers as $manufacturer) {
if ($manufacturer != "") {
echo "<option value='" . $manufacturer . "'>" . $manufacturer . "</option>";
}
}
}
}
Accepted #Mischa Leiss suggestion, changed this messy unique values code:
$manufacturers=array_flip(array_flip(array_reverse($manufacturers,true)));
to his code:
$manufacturers[$product->getAttributeText('manufacturer')] = $product->getAttributeText('manufacturer');
SOLUTION
This is the quickest solution, all thanks to #Mischa
$products = Mage::getResourceModel('catalog/product_collection')
->setStoreId($storeId)
->addAttributeToSelect('manufacturer')
->addAttributeToFilter('status', array('eq' => '1'))
->addAttributeToFilter('visibility', 4)
->addCategoryFilter(Mage::getModel('catalog/category')
->load($sub));
Takes only about 2 seconds.
A. the bottleneck is that you explicitly load each model instead of fetching the data straight from the collection itself - dont get the ids but a collection of products and iterate over it.
B. next thing is, why dont you just add the manufacturer attribute id as array key, so you dont need to array flip.
$manufacturers[$product->getManufacturer()] =
$product->getAttributeText('manufacturer');
C. even better would be to build some custom source model to simply do a smarter sql query.
I assembled a little join series (used color attribute) to get the label/value pair via a product collection:
$collection = Mage::getModel('catalog/product')->getCollection();
//get the color attribute joined
$collection->addAttributeToSelect('color', 'left');
//join the label from the attribute option table
$collection->joinField('color_label', 'eav_attribute_option_value', 'value', 'option_id=color');
//group for uniqueness reset the columns and fetch what we want
$collection->getSelect()->group(array('color_label'));
$collection->getSelect()->reset(Zend_Db_Select::COLUMNS);
$collection->getSelect()->columns(array('color_label' => 'at_color_label.value', 'color_id' => 'at_color_label.option_id'));
Good luck!

How To Get Sub Categories in Magento ?`

I am playing with magento's home page where I have created a tab Which Shows all the categories including Root, categories, and Sub Categories (In One Tab). Now I want to Show Only Main Categories( Whose Parent Is Root) in main Tab And under Each Category I want to List Their respective Sub Categories. I wrote The following Code to achieve a part of this,
MODEL CLASS
public function getsubCategory($parent)
{
$subcategoryCollection = Mage::getModel('catalog/category')
->getCollection()
->addAttributeToFilter('parent_id', $parent);
return $subcategoryCollection;
BLOCK CLASS
protected function b4Html_subcategory($parent)
{
$catModel = Mage::getModel('Pragtech_Sweet/category');
$mysubCategory = $catModel->getsubCategory($parent);
$this->mysubCategory = $myCategory;
return $mysubCategory;
}
TEMPLATE FILE
$obj = new Pragtech_Sweet_Block_Category();
$collection = $obj->b4Html();
foreach ($collection as $category)
{
$name = $category->getName();
$parent = $category->getParent_id();
foreach ($obj->b4Html_subcategory($parent) as $subcategory)
{
$subname = $subcategory->getName();
//Here Will Go Ther Code For Sub Categories
}
but it doesn't work.. I am unable to understand where I am doing wrong... Can anyone help me out
Do this instead :
Mage::getModel('catalog/category')->load('23')->getChildrenCategories();
and iterate over the result.
and that's how i found it out:
$object = Mage::getModel('catalog/category');
print_r(get_class_methods($object));
print_r($object->load('23')->getChildrenCategories()->toArray());
Here's another way to do this, if you don't want to mess with the treeModel stuff, or want more control how categories are loaded:
function getCategoryTree($root_category_name)
{
$categories = Mage::getModel('catalog/category')->getCollection()
->addAttributeToSelect("*")
->addFieldToFilter("Name",array("eq"=>$root_category_name));
$stack = array();
$category = $categories->getFirstItem()->getData();
$categories=array();
array_push($stack, $category);
//regular recursion is boring, let's do a stack
while(count($stack) > 0)
{
$category = array_pop($stack);
array_push($categories, $category);
$children = Mage::getModel('catalog/category')->getCollection()
->addAttributeToSelect("*")
->addFieldToFilter("parent_id",array("eq"=>$category['entity_id']))
->addAttributeToSort("position","desc");
foreach ($children as $child)
{
array_push($stack, $child->getData());
}
}
return $categories;
}
This will give you an array of categories representing the full category tree rooted at the top category with the name "Some Category Name", in order, with all of the category data. Tune this to only select the specific fields you need, instead of "*".
This technique can give you finer grained control of the loading of the category tree, and with how you want the data structured afterwards. For example, you could trivially modify this to do a hierarchical tree instead of a flat array and then serialize that to a JSON object to send to the client.
Add whatever filters you like (active, etc...) at whatever level you like to get even more control.
iterate through this object
Mage::getModel('catalog/category')
->getCollection()
->addAttributeToSelect('*')
->getItems();

Categories