Varien_Data_Collection pagination not working - magento

I have been created a Varien_Data_Collection from scratch:
$myCollection = new Varien_Data_Collection();
after add some items into it, $myCollection->addItem($fooItem);, I tried to paginate it with:
$myCollection->setPageSize(3)->setCurPage(1);
but when I display the collection, it shows all items in the collection instead the first, second o N page.
$myItems = $myCollection->getItems();
foreach ($myItems as $item) {
echo $item->getData('bar');
}

The (non-abstract) base-class Varien_Data_Collection - although it does have the setPageSize and setCurPage methods, those are not reflected within the aggregation of the Iterator:
class Varien_Data_Collection implements IteratorAggregate, Countable
{
...
/**
* Implementation of IteratorAggregate::getIterator()
*/
public function getIterator()
{
$this->load();
return new ArrayIterator($this->_items);
}
...
It will in any case return an ArrayIteator that has all objects. The load method doesn't change a thing here btw:
...
/**
* Load data
*
* #return Varien_Data_Collection
*/
public function loadData($printQuery = false, $logQuery = false)
{
return $this;
}
...
/**
* Load data
*
* #return Varien_Data_Collection
*/
public function load($printQuery = false, $logQuery = false)
{
return $this->loadData($printQuery, $logQuery);
}
...
I'd say this qualifies as a bug, as the public interface makes one assume that the magent collection object Varien_Data_Collection provides pagination while it does not.
I have not searched for a bug-report regarding this issue. The solution is to use another iterator for pagination, for example like outlined in an answer to How to Paginate lines in a foreach loop with PHP with PHP's LimitIterator:
/**
* Class Varien_Data_Collection_Pagination
*
* Aggregates missing Pagination on Collection on getting the Iterator
*
* #author hakre <http://hakre.wordpress.com/>
*/
class Varien_Data_Collection_Pagination implements IteratorAggregate
{
/**
* #var Varien_Data_Collection
*/
private $collection;
public function __construct(Varien_Data_Collection $collection)
{
$this->collection = $collection;
}
public function getIterator()
{
$collection = $this->collection;
if (FALSE === $size = $collection->getPageSize()) {
return $collection;
}
$page = $collection->getCurPage();
if ($page < 1) {
return $collection;
}
$offset = $size * $page - $size;
return new LimitIterator(new IteratorIterator($collection), $offset, $size);
}
}
Usage Example:
$collection = new Varien_Data_Collection();
$collectionIterator = new Varien_Data_Collection_Pagination($collection);
# [...] fill collection
# set pagination:
$collection->setPageSize(3);
$collection->setCurPage(1);
echo iterator_count($collectionIterator); # 3 (int)
Keep in mind that this is mainly an example. IMHO this should be fixed upstream within the Magento codebase, so it's perhaps worth you do some search work in the Magento Bug-Tracker.

As explained in previous post Varien_Data_collection does not support pagination. One solution would be to extend this class and overwrite its loadData() method. Here is practical example of paginating array of stdClass objects. Can be used with any array of data
class YourNamespace_YourModule_Model_History_Collection extends Varien_Data_Collection{
//array of stdClass objects. Can be array of any objects/subarrays
protected $_history = array();
public function loadData($printQuery = false, $logQuery = false){
if ($this->isLoaded()) {
return $this;
}
$this->_totalRecords = count($this->_history);
$this->_setIsLoaded();
// paginate and add items
$from = ($this->getCurPage() - 1) * $this->getPageSize();
$to = $from + $this->getPageSize() ;
$isPaginated = $this->getPageSize() > 0;
$cnt = 0;
$sub_history = array_slice($this->_history, $from, $to-$from);
foreach ( $sub_history as $entry) {
$cnt++;
$item = new $this->_itemObjectClass();
//build here your Varien Object
$item->setDate($entry->Date);
//.......
$this->addItem($item);
if (!$item->hasId()) {
$item->setId($cnt);
}
}
return $this;
}
public function setRecords($records){
if(is_array($records)){
$this->_history = $records;
}
return;
}
}
//Your block class
class YourNamespace_YourModule_Block_History extends Mage_Core_Block_Template{
public function __construct(){
$collection_prepared = new YourNamespace_YourModule_Model_History_Collection();
//get your data here
//$records is array of stdClass objects
$collection_prepared->setRecords($records);
$this->setCollection($collection_prepared);
}
protected function _prepareLayout(){
parent::_prepareLayout();
$pager = $this->getLayout()->createBlock('page/html_pager', 'history.pager');
$pager->setAvailableLimit(array(20=>20,40=>40,80=>80,'all'=>'all'));
$pager->setCollection($this->getCollection());
$this->setChild('pager', $pager);
$this->getCollection()->load();
return $this;
}
public function getPagerHtml()
{
return $this->getChildHtml('pager');
}
}
Hope this helps!

class Pageable_Varien_Data_Collection extends Varien_Data_Collection
{
/**
* Load data
*
* #param bool $printQuery
* #param bool $logQuery
*
* #return Pageable_Varien_Data_Collection
*/
public function load($printQuery = false, $logQuery = false)
{
if ($this->isLoaded()) {
return $this;
}
$this->_renderLimit();
$this->_setIsLoaded();
return $this;
}
/**
* #return Pageable_Varien_Data_Collection
*/
protected function _renderLimit()
{
if ($this->_pageSize) {
$currentPage = $this->getCurPage();
$pageSize = $this->_pageSize;
$firstItem = (($currentPage - 1) * $pageSize + 1);
$lastItem = $firstItem + $pageSize;
$iterator = 1;
foreach ($this->getItems() as $key => $item) {
$pos = $iterator;
$iterator++;
if ($pos >= $firstItem && $pos <= $lastItem) {
continue;
}
$this->removeItemByKey($key);
}
}
return $this;
}
/**
* Retrieve collection all items count
*
* #return int
*/
public function getSize()
{
if (is_null($this->_totalRecords)) {
$this->_totalRecords = count($this->getItems());
}
return intval($this->_totalRecords);
}
/**
* Retrieve collection items
*
* #return array
*/
public function getItems()
{
return $this->_items;
}
}

Related

Custom operation denormalize entity relationships

My Entity(MealSet) has a file association so I have created a custom operation to process the request. My custom operation(MealSetController::class) attempts to denormalize the entity and the returns it. However, because my entity has a relationship, the denormalization fails and throws an error Nested documents for attribute \u0022mealSetIncludes\u0022 are not allowed. Use IRIs instead..
How would one denormalize an entity that has relationships from within a custom operation?
// MealSet entity
namespace App\Entity;
use App\Entity\MealAudienceCategory;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Controller\MealSetController;
use App\Repository\MealSetRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ApiResource(
* itemOperations={
* "get",
* "put"={
* "method"="POST",
* "path" = "/meal-sets/{id}",
* "controller" = MealSetController::class,
* "deserialize" = false
* },
* collectionOperations = {
* "get",
* "post" = {
* "security"="is_granted('CREATE_MENUAUDIENCE')",
* "method"="POST",
* "controller" = MealSetController::class,
* "deserialize" = false,
* }
* }
*)
* #ORM\Entity(repositoryClass=MealSetRepository::class)
* #UniqueEntity(
* fields={"name", "mealAudienceCategory"},
* message="The Meal Set name {{ value }} is already in use."
*)
* #Vich\Uploadable
*/
class MealSet
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=45)
* #Groups({
* "mealset:read",
* "mealset:write",
* "mealaudiencecategory:read",
*})
* #Assert\NotBlank
* #Assert\Length(
* min = 2,
* max = 45,
* minMessage = "Name must be at least {{ limit }} characters long",
* maxMessage = "Name cannot be longer than {{ limit }} characters",
* )
*/
private $name;
/**
* #ORM\Column(type="string", length=255)
* #Groups({
* "mealset:read",
* "mealset:write",
* "mealaudiencecategory:read",
*})
* #Assert\NotBlank
* #Assert\Length(
* min = 2,
* max = 255,
* minMessage = "Description must be at least {{ limit }} characters long",
* maxMessage = "Description cannot be longer than {{ limit }} characters",
* )
*/
private $description;
/**
* #ORM\Column(type="boolean")
* #Groups({
* "mealset:read",
* "mealset:write",
* "mealaudiencecategory:read",
*})
* #Assert\Type(
* type="boolean",
* message="The value {{ value }} is not a valid {{ type }}."
* )
*/
private $published;
/**
* #ORM\Column(type="integer")
* #Groups({
* "mealset:read",
* "mealset:write"
*})
* #Assert\Type(
* type="integer",
* message="The value {{ value }} is not a valid {{ type }}."
* )
*/
private $displayOrder;
/**
* #var MealAudienceCategory
* #ORM\ManyToOne(targetEntity=MealAudienceCategory::class, inversedBy="mealSets")
* #ORM\JoinColumn(nullable=false)
* #Groups({
* "mealset:read",
* "mealset:write"
*})
*/
private $mealAudienceCategory;
/**
* #ORM\OneToMany(targetEntity=MealSetIncludes::class, mappedBy="mealSet", cascade={"persist", "remove"}, orphanRemoval=true)
* #Groups({
* "mealset:read",
* "mealset:write"
*})
*/
private $mealSetIncludes;
/**
* #ORM\OneToMany(targetEntity=MealSetGroup::class, mappedBy="mealSet", orphanRemoval=true)
* #Groups({
* "mealset:read",
* "mealset:write"
*})
*/
private $mealSetGroups;
/**
* #Groups({
* "menuaudience:read",
*})
*/
public $contentUrl;
/**
* #Vich\UploadableField(mapping="media_object", fileNameProperty="filePath")
* #Groups({
* "menuaudience:write",
*})
*/
public $file = null;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $filePath;
/**
* #ORM\Column(type="datetime_immutable")
*/
private $updatedAt;
public function __construct()
{
$this->mealSetSection = new ArrayCollection();
$this->mealSetIncludes = new ArrayCollection();
$this->mealSetGroups = new ArrayCollection();
$this->setUpdatedAt(new \DateTimeImmutable());
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getPublished(): ?bool
{
return $this->published;
}
public function setPublished(bool $published): self
{
$this->published = $published;
return $this;
}
public function getDisplayOrder(): ?int
{
return $this->displayOrder;
}
public function setDisplayOrder(int $displayOrder): self
{
$this->displayOrder = $displayOrder;
return $this;
}
public function getMealAudienceCategory(): ?MealAudienceCategory
{
return $this->mealAudienceCategory;
}
public function setMealAudienceCategory(?MealAudienceCategory $mealAudienceCategory): self
{
$this->mealAudienceCategory = $mealAudienceCategory;
return $this;
}
/**
* #return Collection|MealSetIncludes[]
* #Assert\Valid()
*/
public function getMealSetIncludes(): Collection
{
return $this->mealSetIncludes;
}
public function addMealSetInclude(MealSetIncludes $mealSetInclude): self
{
if (!$this->mealSetIncludes->contains($mealSetInclude)) {
$this->mealSetIncludes[] = $mealSetInclude;
$mealSetInclude->setMealSet($this);
}
return $this;
}
public function removeMealSetInclude(MealSetIncludes $mealSetInclude): self
{
if ($this->mealSetIncludes->contains($mealSetInclude)) {
$this->mealSetIncludes->removeElement($mealSetInclude);
// set the owning side to null (unless already changed)
if ($mealSetInclude->getMealSet() === $this) {
$mealSetInclude->setMealSet(null);
}
}
return $this;
}
/**
* #return Collection|MealSetGroup[]
*/
public function getMealSetGroups(): Collection
{
return $this->mealSetGroups;
}
public function addMealSetGroup(MealSetGroup $mealSetGroup): self
{
if (!$this->mealSetGroups->contains($mealSetGroup)) {
$this->mealSetGroups[] = $mealSetGroup;
$mealSetGroup->setMealSet($this);
}
return $this;
}
public function removeMealSetGroup(MealSetGroup $mealSetGroup): self
{
if ($this->mealSetGroups->removeElement($mealSetGroup)) {
// set the owning side to null (unless already changed)
if ($mealSetGroup->getMealSet() === $this) {
$mealSetGroup->setMealSet(null);
}
}
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getFilePath(): ?string
{
return $this->filePath;
}
public function getContentUrl(): ?string {
return $this->contentUrl;
}
public function setFilePath($filePath): self
{
$this->filePath = $filePath;
return $this;
}
}
class MealSetController extends AbstractController
{
public function __invoke($slug = '', RequestStack $requestStack, MealSetRepository $em): MealSet
{
/*
Determine if they are trying to edit an entity or create an entity.
If edit, find the entity and use that object.
*/
if($slug) {
$obj = $em->findOneBySlug($slug);
if(!$obj)
throw new BadRequestHttpException('Cannot find MealSet where SLUG ='.$slug);
} else {
$obj = new MealSet();
}
$serializer = $this->get("serializer");
$request = $requestStack->getCurrentRequest();
$data = array_filter($request->request->all(), function($x) { return ($x) ? true : false; }); //Grab the post data and filter out any empty strings.
$extractors = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
(new ObjectNormalizer(null, null, null, $extractors))->denormalize($data, MenuAudience::class, 'json', [
AbstractNormalizer::OBJECT_TO_POPULATE => $obj,
AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true
]);
$uploadedFile = $request->files->get('file');
if($uploadedFile)
$obj->setFile($uploadedFile);
return $obj;
}
}

Laravel How can I have if statements in a factory?

Currently trying to have my factory only fill in certain columns based on the product type. Is it even possible to do this in the first place? Thanks in advance.
Here is my code:
class ProductFactory extends Factory
{
protected $model = Product::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
$productType = ProductType::all();
$products = [[
'type_id'=>$productType->random()->id,
'name'=>$this->faker->catchPhrase,
'description'=>$this->faker->paragraph (2, true),
'price'=>$this->faker->randomFloat(2,5,20),
'stock'=>$this->faker->numberBetween(1,100),
]];
foreach($products as $key=>$product){
if($product['type_id'] = 1){
$product[$key]['pagelength'] = $this->faker->randomFloat(3,100,500);
}
elseif($product['type_id'] = 2){
$product[$key]['playlength']=$this->faker->randomFloat(2,40,140);
}
if($product['type_id'] = 3){
$product[$key]['pegi']=$this->faker->randomFloat(1,1,10);
}
}
}
}
The definition method in a factory is responsible to create a single record/instance/array of the model. So the definition shouldn't have a collection $products
Try the below which I guess will help you achieve what you want
class ProductFactory extends Factory
{
protected $model = Product::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
$productTypes = ProductType::all();
$productType = $productTypes->random()->id;
$product = [
'type_id'=>$productType,
'name'=>$this->faker->catchPhrase,
'description'=>$this->faker->paragraph (2, true),
'price'=>$this->faker->randomFloat(2,5,20),
'stock'=>$this->faker->numberBetween(1,100),
];
if($productType === 1) {
$product['pagelength'] = $this->faker->randomFloat(3,100,500);
}
if($productType === 2) {
$product['playlength'] = $this->faker->randomFloat(2,40,140);
}
if($productType === 3) {
$product['pegi'] = $this->faker->randomFloat(1,1,10);
}
return $product;
}
}

Get someone else's User ID for Message

I'm unable to get someone's user ID from their Kunena profile to open a new private message with the recipient name already inserted. The closest I got was the following code, which inserts my own username...
defined('_JEXEC') or die ();
class KunenaPrivateUddeIM extends KunenaPrivate
{
protected $uddeim = null;
protected $params = null;
/**
* #param $params
*/
public function __construct($params)
{
$this->params = $params;
if (!class_exists('uddeIMAPI'))
{
return;
}
$this->uddeim = new uddeIMAPI();
if ($this->uddeim->version() < 1)
{
return;
}
}
/**
* #param $userid
*
* #return string
*/
protected function getURL($userid)
{
static $itemid = false;
if ($itemid === false)
{
$itemid = 0;
if (method_exists($this->uddeim, 'getItemid'))
{
$itemid = $this->uddeim->getItemid();
}
if ($itemid)
{
$itemid = '&Itemid=' . (int) $itemid;
}
else
{
$itemid = '';
}
}
return JRoute::_('index.php?option=com_uddeim&task=new&recip=' . (int) $userid . $itemid);
}
/**
* #param $userid
*
* #return mixed
*/
public function getUnreadCount($userid)
{
return $this->uddeim->getInboxUnreadMessages($userid);
}
/**
* #param $text
*
* #return string
*/
public function getInboxLink($text)
{
if (!$text)
{
$text = JText::_('COM_KUNENA_PMS_INBOX');
}
return '' . $text . '';
}
/**
* #return string
*/
public function getInboxURL()
{
$user = JFactory::getUser($userid);
return JRoute::_('index.php?option=com_uddeim&task=new&recip=' . ($user ->id));
}
}
Change this line:
return JRoute::_('index.php?option=com_uddeim&task=new&recip=' . (JFactory::getUser()->id));
to the following 2 lines:
$user = JFactory::getUser($userid);
return JRoute::_('index.php?option=com_uddeim&task=new&recip=' . ($user ->id));
You can check this post that we have written some time ago (it was written for Joomla 2.5, but it still works, except that you have to remove the &) on how to retrieve non-cached users in Joomla: http://www.itoctopus.com/how-to-retrieve-the-non-cached-user-information-from-joomla
Ok kunena developer has a hotfix on github for the upcomming release update. Here is the commit link https://github.com/Kunena/Kunena-Forum/pull/3547

Fatal error: Call to a member function setFinalPrice() on a non-object in /app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php on line 89

I had installed an custom extension. Its running successfully and had all files at its place. But I am getting this error on my listing page
Fatal error: Call to a member function setFinalPrice() on a non-object in /var/zpanel/hostdata/zadmin/public_html/unisport_com/app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php on line 89.
I google this error but did'nt found any proper solution. One solution is to copy the original Abstract.php file may be because extension had overwrite this file. But I checked the code from the backup its the same and also copied original file but nothing worked for me.
What is wrong with this file? Can anybody help?
below is the code of Abstract.php file:
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license#magentocommerce.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magentocommerce.com for more information.
*
* #category Mage
* #package Mage_Sales
* #copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com)
* #license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* Quote item abstract model
*
* Price attributes:
* - price - initial item price, declared during product association
* - original_price - product price before any calculations
* - calculation_price - prices for item totals calculation
* - custom_price - new price that can be declared by user and recalculated during calculation process
* - original_custom_price - original defined value of custom price without any convertion
*
* #category Mage
* #package Mage_Sales
* #author Magento Core Team <core#magentocommerce.com>
*/
abstract class Mage_Sales_Model_Quote_Item_Abstract extends Mage_Core_Model_Abstract
implements Mage_Catalog_Model_Product_Configuration_Item_Interface
{
/**
* Parent item for sub items for bundle product, configurable product, etc.
*
* #var Mage_Sales_Model_Quote_Item_Abstract
*/
protected $_parentItem = null;
/**
* Children items in bundle product, configurable product, etc.
*
* #var array
*/
protected $_children = array();
/**
*
* #var array
*/
protected $_messages = array();
/**
* Retrieve Quote instance
*
* #return Mage_Sales_Model_Quote
*/
abstract function getQuote();
/**
* Retrieve product model object associated with item
*
* #return Mage_Catalog_Model_Product
*/
public function getProduct()
{
$product = $this->_getData('product');
if ($product === null && $this->getProductId()) {
$product = Mage::getModel('catalog/product')
->setStoreId($this->getQuote()->getStoreId())
->load($this->getProductId());
$this->setProduct($product);
}
/**
* Reset product final price because it related to custom options
*/
$product->setFinalPrice(null);
if (is_array($this->_optionsByCode)) {
$product->setCustomOptions($this->_optionsByCode);
}
return $product;
}
/**
* Returns special download params (if needed) for custom option with type = 'file'
* Needed to implement Mage_Catalog_Model_Product_Configuration_Item_Interface.
* Return null, as quote item needs no additional configuration.
*
* #return null|Varien_Object
*/
public function getFileDownloadParams()
{
return null;
}
/**
* Specify parent item id before saving data
*
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
protected function _beforeSave()
{
parent::_beforeSave();
if ($this->getParentItem()) {
$this->setParentItemId($this->getParentItem()->getId());
}
return $this;
}
/**
* Set parent item
*
* #param Mage_Sales_Model_Quote_Item $parentItem
* #return Mage_Sales_Model_Quote_Item
*/
public function setParentItem($parentItem)
{
if ($parentItem) {
$this->_parentItem = $parentItem;
$parentItem->addChild($this);
}
return $this;
}
/**
* Get parent item
*
* #return Mage_Sales_Model_Quote_Item
*/
public function getParentItem()
{
return $this->_parentItem;
}
/**
* Get chil items
*
* #return array
*/
public function getChildren()
{
return $this->_children;
}
/**
* Add child item
*
* #param Mage_Sales_Model_Quote_Item_Abstract $child
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function addChild($child)
{
$this->setHasChildren(true);
$this->_children[] = $child;
return $this;
}
/**
* Adds message(s) for quote item. Duplicated messages are not added.
*
* #param mixed $messages
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function setMessage($messages)
{
$messagesExists = $this->getMessage(false);
if (!is_array($messages)) {
$messages = array($messages);
}
foreach ($messages as $message) {
if (!in_array($message, $messagesExists)) {
$this->addMessage($message);
}
}
return $this;
}
/**
* Add message of quote item to array of messages
*
* #param string $message
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function addMessage($message)
{
$this->_messages[] = $message;
return $this;
}
/**
* Get messages array of quote item
*
* #param bool $string flag for converting messages to string
* #return array|string
*/
public function getMessage($string = true)
{
if ($string) {
return join("\n", $this->_messages);
}
return $this->_messages;
}
/**
* Removes message by text
*
* #param string $text
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function removeMessageByText($text)
{
foreach ($this->_messages as $key => $message) {
if ($message == $text) {
unset($this->_messages[$key]);
}
}
return $this;
}
/**
* Clears all messages
*
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function clearMessage()
{
$this->unsMessage(); // For older compatibility, when we kept message inside data array
$this->_messages = array();
return $this;
}
/**
* Retrieve store model object
*
* #return Mage_Core_Model_Store
*/
public function getStore()
{
return $this->getQuote()->getStore();
}
/**
* Checking item data
*
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function checkData()
{
$this->setHasError(false);
$this->clearMessage();
$qty = $this->_getData('qty');
try {
$this->setQty($qty);
} catch (Mage_Core_Exception $e) {
$this->setHasError(true);
$this->setMessage($e->getMessage());
} catch (Exception $e) {
$this->setHasError(true);
$this->setMessage(Mage::helper('sales')->__('Item qty declaration error.'));
}
try {
$this->getProduct()->getTypeInstance(true)->checkProductBuyState($this->getProduct());
} catch (Mage_Core_Exception $e) {
$this->setHasError(true)
->setMessage($e->getMessage());
$this->getQuote()->setHasError(true)
->addMessage(Mage::helper('sales')->__('Some of the products below do not have all the required options.'));
} catch (Exception $e) {
$this->setHasError(true)
->setMessage(Mage::helper('sales')->__('Item options declaration error.'));
$this->getQuote()->setHasError(true)
->addMessage(Mage::helper('sales')->__('Items options declaration error.'));
}
if ($this->getProduct()->getHasError()) {
$this->setHasError(true)
->setMessage(Mage::helper('sales')->__('Some of the selected options are not currently available.'));
$this->getQuote()->setHasError(true)
->addMessage($this->getProduct()->getMessage(), 'options');
}
if ($this->getHasConfigurationUnavailableError()) {
$this->setHasError(true)
->setMessage(Mage::helper('sales')->__('Selected option(s) or their combination is not currently available.'));
$this->getQuote()->setHasError(true)
->addMessage(Mage::helper('sales')->__('Some item options or their combination are not currently available.'), 'unavailable-configuration');
$this->unsHasConfigurationUnavailableError();
}
return $this;
}
/**
* Get original (not related with parent item) item quantity
*
* #return int|float
*/
public function getQty()
{
return $this->_getData('qty');
}
/**
* Get total item quantity (include parent item relation)
*
* #return int|float
*/
public function getTotalQty()
{
if ($this->getParentItem()) {
return $this->getQty()*$this->getParentItem()->getQty();
}
return $this->getQty();
}
/**
* Calculate item row total price
*
* #return Mage_Sales_Model_Quote_Item
*/
public function calcRowTotal()
{
$qty = $this->getTotalQty();
// Round unit price before multiplying to prevent losing 1 cent on subtotal
$total = $this->getStore()->roundPrice($this->getCalculationPriceOriginal()) * $qty;
$baseTotal = $this->getStore()->roundPrice($this->getBaseCalculationPriceOriginal()) * $qty;
$this->setRowTotal($this->getStore()->roundPrice($total));
$this->setBaseRowTotal($this->getStore()->roundPrice($baseTotal));
return $this;
}
/**
* Get item price used for quote calculation process.
* This method get custom price (if it is defined) or original product final price
*
* #return float
*/
public function getCalculationPrice()
{
$price = $this->_getData('calculation_price');
if (is_null($price)) {
if ($this->hasCustomPrice()) {
$price = $this->getCustomPrice();
} else {
$price = $this->getConvertedPrice();
}
$this->setData('calculation_price', $price);
}
return $price;
}
/**
* Get item price used for quote calculation process.
* This method get original custom price applied before tax calculation
*
* #return float
*/
public function getCalculationPriceOriginal()
{
$price = $this->_getData('calculation_price');
if (is_null($price)) {
if ($this->hasOriginalCustomPrice()) {
$price = $this->getOriginalCustomPrice();
} else {
$price = $this->getConvertedPrice();
}
$this->setData('calculation_price', $price);
}
return $price;
}
/**
* Get calculation price used for quote calculation in base currency.
*
* #return float
*/
public function getBaseCalculationPrice()
{
if (!$this->hasBaseCalculationPrice()) {
if ($this->hasCustomPrice()) {
$price = (float) $this->getCustomPrice();
if ($price) {
$rate = $this->getStore()->convertPrice($price) / $price;
$price = $price / $rate;
}
} else {
$price = $this->getPrice();
}
$this->setBaseCalculationPrice($price);
}
return $this->_getData('base_calculation_price');
}
/**
* Get original calculation price used for quote calculation in base currency.
*
* #return float
*/
public function getBaseCalculationPriceOriginal()
{
if (!$this->hasBaseCalculationPrice()) {
if ($this->hasOriginalCustomPrice()) {
$price = (float) $this->getOriginalCustomPrice();
if ($price) {
$rate = $this->getStore()->convertPrice($price) / $price;
$price = $price / $rate;
}
} else {
$price = $this->getPrice();
}
$this->setBaseCalculationPrice($price);
}
return $this->_getData('base_calculation_price');
}
/**
* Get whether the item is nominal
* TODO: fix for multishipping checkout
*
* #return bool
*/
public function isNominal()
{
if (!$this->hasData('is_nominal')) {
$this->setData('is_nominal', $this->getProduct() ? '1' == $this->getProduct()->getIsRecurring() : false);
}
return $this->_getData('is_nominal');
}
/**
* Data getter for 'is_nominal'
* Used for converting item to order item
*
* #return int
*/
public function getIsNominal()
{
return (int)$this->isNominal();
}
/**
* Get original price (retrieved from product) for item.
* Original price value is in quote selected currency
*
* #return float
*/
public function getOriginalPrice()
{
$price = $this->_getData('original_price');
if (is_null($price)) {
$price = $this->getStore()->convertPrice($this->getBaseOriginalPrice());
$this->setData('original_price', $price);
}
return $price;
}
/**
* Set original price to item (calculation price will be refreshed too)
*
* #param float $price
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function setOriginalPrice($price)
{
return $this->setData('original_price', $price);
}
/**
* Get Original item price (got from product) in base website currency
*
* #return float
*/
public function getBaseOriginalPrice()
{
return $this->_getData('base_original_price');
}
/**
* Specify custom item price (used in case whe we have apply not product price to item)
*
* #param float $value
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function setCustomPrice($value)
{
$this->setCalculationPrice($value);
$this->setBaseCalculationPrice(null);
return $this->setData('custom_price', $value);
}
/**
* Get item price. Item price currency is website base currency.
*
* #return decimal
*/
public function getPrice()
{
return $this->_getData('price');
}
/**
* Specify item price (base calculation price and converted price will be refreshed too)
*
* #param float $value
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function setPrice($value)
{
$this->setBaseCalculationPrice(null);
$this->setConvertedPrice(null);
return $this->setData('price', $value);
}
/**
* Get item price converted to quote currency
* #return float
*/
public function getConvertedPrice()
{
$price = $this->_getData('converted_price');
if (is_null($price)) {
$price = $this->getStore()->convertPrice($this->getPrice());
$this->setData('converted_price', $price);
}
return $price;
}
/**
* Set new value for converted price
* #param float $value
* #return Mage_Sales_Model_Quote_Item_Abstract
*/
public function setConvertedPrice($value)
{
$this->setCalculationPrice(null);
$this->setData('converted_price', $value);
return $this;
}
/**
* Clone quote item
*
* #return Mage_Sales_Model_Quote_Item
*/
public function __clone()
{
$this->setId(null);
$this->_parentItem = null;
$this->_children = array();
$this->_messages = array();
return $this;
}
/**
* Checking if there children calculated or parent item
* when we have parent quote item and its children
*
* #return bool
*/
public function isChildrenCalculated()
{
if ($this->getParentItem()) {
$calculate = $this->getParentItem()->getProduct()->getPriceType();
} else {
$calculate = $this->getProduct()->getPriceType();
}
if ((null !== $calculate) && (int)$calculate === Mage_Catalog_Model_Product_Type_Abstract::CALCULATE_CHILD) {
return true;
}
return false;
}
/**
* Checking can we ship product separatelly (each child separately)
* or each parent product item can be shipped only like one item
*
* #return bool
*/
public function isShipSeparately()
{
if ($this->getParentItem()) {
$shipmentType = $this->getParentItem()->getProduct()->getShipmentType();
} else {
$shipmentType = $this->getProduct()->getShipmentType();
}
if ((null !== $shipmentType) &&
(int)$shipmentType === Mage_Catalog_Model_Product_Type_Abstract::SHIPMENT_SEPARATELY) {
return true;
}
return false;
}
/**
* Calculate item tax amount
*
* #deprecated logic moved to tax totals calculation model
* #return Mage_Sales_Model_Quote_Item
*/
public function calcTaxAmount()
{
$store = $this->getStore();
if (!Mage::helper('tax')->priceIncludesTax($store)) {
if (Mage::helper('tax')->applyTaxAfterDiscount($store)) {
$rowTotal = $this->getRowTotalWithDiscount();
$rowBaseTotal = $this->getBaseRowTotalWithDiscount();
} else {
$rowTotal = $this->getRowTotal();
$rowBaseTotal = $this->getBaseRowTotal();
}
$taxPercent = $this->getTaxPercent()/100;
$this->setTaxAmount($store->roundPrice($rowTotal * $taxPercent));
$this->setBaseTaxAmount($store->roundPrice($rowBaseTotal * $taxPercent));
$rowTotal = $this->getRowTotal();
$rowBaseTotal = $this->getBaseRowTotal();
$this->setTaxBeforeDiscount($store->roundPrice($rowTotal * $taxPercent));
$this->setBaseTaxBeforeDiscount($store->roundPrice($rowBaseTotal * $taxPercent));
} else {
if (Mage::helper('tax')->applyTaxAfterDiscount($store)) {
$totalBaseTax = $this->getBaseTaxAmount();
$totalTax = $this->getTaxAmount();
if ($totalTax && $totalBaseTax) {
$totalTax -= $this->getDiscountAmount() * ($this->getTaxPercent() / 100);
$totalBaseTax -= $this->getBaseDiscountAmount() * ($this->getTaxPercent() / 100);
$this->setBaseTaxAmount($store->roundPrice($totalBaseTax));
$this->setTaxAmount($store->roundPrice($totalTax));
}
}
}
if (Mage::helper('tax')->discountTax($store) && !Mage::helper('tax')->applyTaxAfterDiscount($store)) {
if ($this->getDiscountPercent()) {
$baseTaxAmount = $this->getBaseTaxBeforeDiscount();
$taxAmount = $this->getTaxBeforeDiscount();
$baseDiscountDisposition = $baseTaxAmount/100*$this->getDiscountPercent();
$discountDisposition = $taxAmount/100*$this->getDiscountPercent();
$this->setDiscountAmount($this->getDiscountAmount()+$discountDisposition);
$this->setBaseDiscountAmount($this->getBaseDiscountAmount()+$baseDiscountDisposition);
}
}
return $this;
}
/**
* Get item tax amount
*
* #deprecated
* #return decimal
*/
public function getTaxAmount()
{
return $this->_getData('tax_amount');
}
/**
* Get item base tax amount
*
* #deprecated
* #return decimal
*/
public function getBaseTaxAmount()
{
return $this->_getData('base_tax_amount');
}
/**
* Get item price (item price always exclude price)
*
* #deprecated
* #return decimal
*/
protected function _calculatePrice($value, $saveTaxes = true)
{
$store = $this->getQuote()->getStore();
if (Mage::helper('tax')->priceIncludesTax($store)) {
$bAddress = $this->getQuote()->getBillingAddress();
$sAddress = $this->getQuote()->getShippingAddress();
$address = $this->getAddress();
if ($address) {
switch ($address->getAddressType()) {
case Mage_Sales_Model_Quote_Address::TYPE_BILLING:
$bAddress = $address;
break;
case Mage_Sales_Model_Quote_Address::TYPE_SHIPPING:
$sAddress = $address;
break;
}
}
if ($this->getProduct()->getIsVirtual()) {
$sAddress = $bAddress;
}
$priceExcludingTax = Mage::helper('tax')->getPrice(
$this->getProduct()->setTaxPercent(null),
$value,
false,
$sAddress,
$bAddress,
$this->getQuote()->getCustomerTaxClassId(),
$store
);
$priceIncludingTax = Mage::helper('tax')->getPrice(
$this->getProduct()->setTaxPercent(null),
$value,
true,
$sAddress,
$bAddress,
$this->getQuote()->getCustomerTaxClassId(),
$store
);
if ($saveTaxes) {
$qty = $this->getQty();
if ($this->getParentItem()) {
$qty = $qty*$this->getParentItem()->getQty();
}
if (Mage::helper('tax')->displayCartPriceInclTax($store)) {
$rowTotal = $value*$qty;
$rowTotalExcTax = Mage::helper('tax')->getPrice(
$this->getProduct()->setTaxPercent(null),
$rowTotal,
false,
$sAddress,
$bAddress,
$this->getQuote()->getCustomerTaxClassId(),
$store
);
$rowTotalIncTax = Mage::helper('tax')->getPrice(
$this->getProduct()->setTaxPercent(null),
$rowTotal,
true,
$sAddress,
$bAddress,
$this->getQuote()->getCustomerTaxClassId(),
$store
);
$totalBaseTax = $rowTotalIncTax-$rowTotalExcTax;
$this->setRowTotalExcTax($rowTotalExcTax);
}
else {
$taxAmount = $priceIncludingTax - $priceExcludingTax;
$this->setTaxPercent($this->getProduct()->getTaxPercent());
$totalBaseTax = $taxAmount*$qty;
}
$totalTax = $this->getStore()->convertPrice($totalBaseTax);
$this->setTaxBeforeDiscount($totalTax);
$this->setBaseTaxBeforeDiscount($totalBaseTax);
$this->setTaxAmount($totalTax);
$this->setBaseTaxAmount($totalBaseTax);
}
$value = $priceExcludingTax;
}
return $value;
}
}

Event dispatcher in silex

I need to replace my notifyService, with the event dispatcher of Symphony.
Here is the original service:
<?php
class VmService
{
/**
* #var VmManager|null
*/
protected $vmManager = null;
/**
* #var ProvisionerInterface[]
*/
protected $provisionners = array();
/**
* #var NotifyService|null
*/
protected $notifyService = null;
public function setVmManager(VmManager $vmManager)
{
$this->vmManager = $vmManager;
}
public function getVmManager()
{
return $this->vmManager;
}
/**
* #param $type
* #param ProvisionerInterface $provisionner
*/
public function setProvisionner($type, ProvisionerInterface $provisionner)
{
$this->provisionners[$type] = $provisionner;
}
/**
* #param $type
* #return ProvisionerInterface
*/
public function getProvisionner(Vm $vm)
{
return $this->provisionners[$vm->getType()];
}
/**
* #param NotifyService $notifyService
*/
public function setNotifyService(NotifyService $notifyService)
{
$this->notifyService = $notifyService;
}
/**
* #return NotifyService|null
*/
public function getNotifyService()
{
return $this->notifyService;
}
public function initialise(Vm $vm)
{
$vmManager = $this->getVmManager();
$provisioner = $this->getProvisionner($vm);
$provisioner->initialise($vm);
$vm->setStatus(VM::STOPPED);
$vmManager->flush($vm);
}
public function delete(Vm $vm, $force = false)
{
$now = new \DateTime();
$day = $now->format('w');
if ( ($day == 0 || $day == 6) && ! $force) {
throw new \Exception('Cannot delete a VM on weekend unless you force it');
}
$vmManager = $this->getVmManager();
$provisioner = $this->getProvisionner($vm);
$provisioner->delete($vm);
$vm->setStatus(Vm::STOPPED);
$vmManager->flush($vm);
}
private function deleteLogFile(Vm $vm)
{
$filename = VmLogger::getLogFile($vm->getIdVm());
if (file_exists($filename)) {
#unlink("$filename");
}
}
public function prepare(Vm $vm)
{
/**
* #var VM $vm
*/
$provisionner = $this->getProvisionner($vm);
//$provisionner->start($vm, true, 'integ.lafourchette.local');
$provisionner->stop($vm);
}
public function start(Vm $vm, $provisionEnable = true)
{
$vmManager = $this->getVmManager();
$notify = $this->getNotifyService();
/**
* #var VM $vm
*/
$provisionner = $this->getProvisionner($vm);
$vm->setStatus(VM::STARTED);
$vmManager->flush($vm);
try {
$provisionner->start($vm, $provisionEnable);
$vm->setStatus(VM::RUNNING);
$vmManager->flush($vm);
$notify->send('ready', $vm);
} catch (UnableToStartException $e) {
$vm->setStatus(VM::STOPPED);
$vmManager->flush($vm);
$notify->send('unable_to_start', $vm);
throw $e;
}
}
public function getStatus(Vm $vm)
{
return $this->getProvisionner($vm)->getStatus($vm);
}
public function stop(Vm $vm)
{
$vmManager = $this->getVmManager();
/**
* #var VM $vm
*/
$provisionner = $this->getProvisionner($vm);
$vm->setStatus(Vm::STOPPED);
$vmManager->flush($vm);
$provisionner->stop($vm);
}
public function archived(Vm $vm)
{
$vmManager = $this->getVmManager();
$this->delete($vm);
$vm->setStatus(VM::EXPIRED);
$this->deleteLogFile($vm);
$vmManager->flush($vm);
$this->prepare($vm);
}
}
And here is what I've changed:
<?php
class VmService
{
/**
* #var VmManager|null
*/
protected $vmManager = null;
/**
* #var ProvisionerInterface[]
*/
protected $provisionners = array();
/**
* #var NotifyService|null
*/
protected $notifyService = null;
public function setVmManager(VmManager $vmManager)
{
$this->vmManager = $vmManager;
}
public function getVmManager()
{
return $this->vmManager;
}
/**
* #param $type
* #param ProvisionerInterface $provisionner
*/
public function setProvisionner($type, ProvisionerInterface $provisionner)
{
$this->provisionners[$type] = $provisionner;
}
/**
* #param $type
* #return ProvisionerInterface
*/
public function getProvisionner(Vm $vm)
{
return $this->provisionners[$vm->getType()];
}
/**
* #param NotifyService $notifyService
*/
public function setNotifyService(NotifyService $notifyService)
{
$this->notifyService = $notifyService;
}
/**
* #return NotifyService|null
*/
public function getNotifyService()
{
return $this->notifyService;
}
public function initialise(Vm $vm)
{
$vmManager = $this->getVmManager();
$provisioner = $this->getProvisionner($vm);
$provisioner->initialise($vm);
$vm->setStatus(VM::STOPPED);
$vmManager->flush($vm);
}
public function delete(Vm $vm, $force = false)
{
$now = new \DateTime();
$day = $now->format('w');
if ( ($day == 0 || $day == 6) && ! $force) {
throw new \Exception('Cannot delete a VM on weekend unless you force it');
}
$vmManager = $this->getVmManager();
$provisioner = $this->getProvisionner($vm);
$provisioner->delete($vm);
$vm->setStatus(Vm::STOPPED);
$vmManager->flush($vm);
}
private function deleteLogFile(Vm $vm)
{
$filename = VmLogger::getLogFile($vm->getIdVm());
if (file_exists($filename)) {
#unlink("$filename");
}
}
public function prepare(Vm $vm)
{
/**
* #var VM $vm
*/
$provisionner = $this->getProvisionner($vm);
//$provisionner->start($vm, true, 'integ.lafourchette.local');
$provisionner->stop($vm);
}
public function start(Vm $vm, $provisionEnable = true)
{
$vmManager = $this->getVmManager();
$dispatcher = new EventDispatcher();
/**
* #var VM $vm
*/
$provisionner = $this->getProvisionner($vm);
$vm->setStatus(VM::STARTED);
$vmManager->flush($vm);
try {
$provisionner->start($vm, $provisionEnable);
$vm->setStatus(VM::RUNNING);
$vmManager->flush($vm);
$event = new NotifyEvent($vm);
$dispatcher->addListener('notify.action', $event);
$dispatcher->dispatch('notify.action');
} catch (UnableToStartException $e) {
$vm->setStatus(VM::STOPPED);
$vmManager->flush($vm);
$event = new NotifyEvent($vm);
$dispatcher->addListener('notify.action', $event);
$dispatcher->dispatch('notify.action');
throw $e;
}
}
public function getStatus(Vm $vm)
{
return $this->getProvisionner($vm)->getStatus($vm);
}
public function stop(Vm $vm)
{
$vmManager = $this->getVmManager();
/**
* #var VM $vm
*/
$provisionner = $this->getProvisionner($vm);
$vm->setStatus(Vm::STOPPED);
$vmManager->flush($vm);
$provisionner->stop($vm);
}
public function archived(Vm $vm)
{
$vmManager = $this->getVmManager();
$this->delete($vm);
$vm->setStatus(VM::EXPIRED);
$this->deleteLogFile($vm);
$vmManager->flush($vm);
$this->prepare($vm);
}
}
However as I see with the documentation, I need a listener but I can't figure out how to relate it, and how to make it to work.
You need to refactor your code. As stated in the documentation, in order to dispatch an event, you have to create the event and call the dispatch method on the EventDispatcher instance, so in your code instead of what you are doing currently:
<?php
// this is the start method of your service
// ...
$event = new NotifyEvent($vm);
$dispatcher->addListener('notify.action', $event);
$dispatcher->dispatch('notify.action');
// ...
you have to create the event and dispatch it directly:
<?php
// this is the start method of your service
// ...
$event = new NotifyEvent($vm);
$dispatcher->dispatch('notify.action', $event);
// ...
You also are sending the same event ('notify.action') now but previously you had 2 different events: 'ready' and 'unable_to_start', so you have to create 2 listeners for 2 differents events ('notify.success' and 'notify.unable_to_start' for example).
Now you have 2 more problems:
No listeners are configured for your 'notify.action' event, so you have to add listeners somewhere (you've tried but you've failed, see the documentation for the EventDispatcher component for more details about how to properly configure a listener)
You're creating the dispatcher every time the start method is created, so you have to configure it every time also (configure means add the listeners)
You can tackle both if you refactor a little bit by creating another service based on the Symfony EventDispatcher:
<?php
// somewhere in your config file
// ...
$app['notifyService'] = $app->share(function() use ($app) {
$dispatcher = new Symfony\Component\EventDispatcher\EventDispatcher();
$dispatcher->addListener('notify.success', $callable1);
$dispatcher->addListener('notify.unable_to_start', $callable2);
return $dispatcher;
});
Notice that $callable1 and $callable2 are there to give you an idea, again check the documentation to see how to add listeners properly (you can create a clousure or a method in a class that handles the events, it's completely up to you).
Now you've defined a notifyService based on the Event Dispatcher (another one completely different from the EventDispatcher used by Silex so you have a clean Event Dispatcher for your domain events), you can use it as a notify service on your class. You'd do it like before: using the setNotifyService method and in your code you just need to create the event and call the dispatch method (assuming you've already called the setNotifyService):
<?php
// class VmService
// method start
// when you've to dispatch the success event
$event = new NotifySuccessEvent($vm);
$this->notifyService->dispatch('notify.success', $event);
// if you have to dispatch the notify.unable_to_start event
$event = new NotifyUnableToStartEvent($vm);
$this->notifyService->dispatch('notify.unable_to_start', $event);
I hope that this will put you on the right track.
PS: You'll have to code the 2 event classes by yourself, again, check the docs for details.

Resources