I new in ZF and have a code located below. I try get custom colums and join in sql select, but it failed.
I tried to use the search but found no results.
Tell me examples of how to do more complex queries.
Thanks.
<?php
namespace FcFlight\Model;
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Adapter\Adapter;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\Sql\Select;
use FcFlight\Filter\FlightHeaderFilter;
class FlightHeaderModel extends AbstractTableGateway
{
/**
* #var string
*/
protected $table = 'flightBaseHeaderForm';
/**
* #param \Zend\Db\Adapter\Adapter $adapter
*/
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
$this->resultSetPrototype = new ResultSet();
$this->resultSetPrototype->setArrayObjectPrototype(new FlightHeaderFilter($this->adapter));
$this->initialize();
}
/**
* #param $id
* #return array|\ArrayObject|null
* #throws \Exception
*/
public function get($id)
{
$id = (int)$id;
$rowSet = $this->select(array('id' => $id));
$row = $rowSet->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
$row->dateOrder = date('Y-m-d', $row->dateOrder);
return $row;
}
}
Since you are using the tableGateway, you must get an instance of Sql(): $this->getSql();
With that instance you will create an instance of Select, which will then let you perform a join:
$sql = $this->getSql();
$select = $sql->select();
$select->join('TableNameToJoin', 'MainColumnA = JoinColumnA');
Then to execute the query, you call selectWith on your tableGateway:
$this->selectWith($select);
If you wish to customize the join further, you can pass two more arugments:
An array of columns you wish to select
The type of join you wish to do (LEFT JOIN for example)
You can also add an alias to the table you are joining on by passing an Array as the table name, with the key of the array being the alias and the value being the table name.
$select->join(['Alias' => 'TableNameToJoin'], 'MainColumnA = Alias.JoinColumnA', ['ColumnA', 'ColumnB'], $select::JOIN_LEFT);
Related
I'm having an issue with Zend Framework 2's SQL class. It's saying it cannot find a certain column in the table but the column is there. I've run into this problem before and had to use actual SQL syntax instead of building the query. I rather not have to resort to putting the actual syntax in and instead figure out why this is happening so I can avoid having this issue in the future.
Here is my code:
namespace Members\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Sql\Sql;
use Zend\Db\Sql\Select;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Predicate;
use Members\Model\Interfaces\FeedInterface;
use Members\Model\Exceptions\FeedException;
class FeedModel implements FeedInterface
{
/**
* #var TableGateway
*/
public $gateway;
/**
* #var string
*/
public $user;
/**
* #var Sql
*/
public $sql;
/**
* #var Select
*/
public $select;
/**
* Constructor method for FeedModel class
* #param TableGateway $gateway
* #param string $user
*/
public function __construct(TableGateway $gateway, $user)
{
$this->gateway = $gateway instanceof TableGateway ? $gateway : null;
$this->select = new Select();
$this->sql = new Sql($this->gateway->getAdapter());
$this->user = $user;
}
/**
* {#inheritDoc}
* #see \Members\Model\Interfaces\FeedInterface::listFriendsStatus()
*/
public function listFriendsStatus()
{
// get the friend ids based on user id
// and then compare the friend id to the id in status table
$friend_query = $this->select->columns(array('*'))
->from('friends')
->where(array('user_id' => $this->getUserId()['id'])); // the issue
file_put_contents(getcwd() . '/public/query.txt', $this->sql->buildSqlString($friend_query));
$query = $this->sql->getAdapter()->query(
$this->sql->buildSqlString($friend_query),
Adapter::QUERY_MODE_EXECUTE
);
if ($query->count() > 0) {
$friend_id = array();
foreach ($query as $result) {
$friend_id[] = $result;
}
$this->select->columns(array('status'))
->from('status')
->where(array('id' => array_values($friend_id), new Predicate\IsNotNull('id')));
$status_query = $this->sql->getAdapter()->query(
$this->sql->buildSqlString($this->select),
Adapter::QUERY_MODE_EXECUTE
);
if ($status_query->count() > 0) {
// check if a image was used
$this->select->columns('username')
->from('members')
->where(array('id' => array_values($friend_id), new Predicate\IsNotNull('id')));
$image_query = $this->sql->getAdapter()->query(
$this->sql->buildSqlString($this->select),
Adapter::QUERY_MODE_EXECUTE
);
if ($image_query->count() > 0) {
$status_dir = array();
foreach ($image_query as $value) {
if (#!is_dir(getcwd() . '/public/images/profile/' . $value['username'] . '/status/')) {
continue;
}
$status_dir[] = getcwd() . '/public/images/profile/' . $value['username'] . '/status/';
}
$images = array();
// retrieve the image inside the status directory
foreach (array_diff(scandir($status_dir, 1), array('.', '..')) as $values) {
$images[] = $values;
}
} else {
throw new FeedException("The user does not exist in the user table.");
}
$status = array();
// get all the statuses
foreach ($status_query as $rows) {
$status[] = $rows;
}
return array('status' => $status, 'images' => $images);
} else {
throw new FeedException("No status was found for your friends.");
}
} else {
throw new FeedException(sprintf("Could not locate any friends for %s", $this->user));
}
}
/**
* {#inheritDoc}
* #see \Members\Model\Interfaces\FeedInterface::hideFriendsStatus()
*/
public function hideFriendsStatus($friend_id)
{
}
/**
* Grabs the user id for the user
*
* #return int|boolean
*/
public function getUserId()
{
$this->select->columns(array('*'))
->from('members')
->where(array('username' => $this->user));
$query = $this->sql->getAdapter()->query(
$this->sql->buildSqlString($this->select),
Adapter::QUERY_MODE_EXECUTE
);
if ($query->count() > 0) {
foreach ($query as $result) {
$row = $result;
}
return $row;
}
return false;
}
}
This is the exception message I am getting:
PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user_id' in 'where clause'
But as you can see in this screenshot, the column user_id exists in the friends table I have in place:
So my question is why is it doing this and how can I in the future avoid this issue?
Thanks!
It looks like the select is causing the issue.
Since your code ->from('friends') is called first and then it is overridden due to this function call $this->getUserId(), which overrides the friends table to members due to ->from('members').
Try changing your code to.
$userId = $this->getUserId()['id'];
$friend_query = $this->select->columns(array('*'))
->from('friends')
->where(array('user_id' => $userId));
This should work, but if it doesn't, try to just create new select object $select = new Select(); in both the functions rather than $this->select = new Select();.
Working with PHPUnit and Doctrine I frequently end up writing very large methods for mocking Doctrines ClassMetadata, although in my opinion it does not need to be mocked, because it can be seen as stable. Still I need to mock the EntityManager because I don't want Doctrine to connect to a database.
So here's my question: How do I get my ClassMetadata via the EntityManager mock without needing a database connection? For all eventual database calls the EntityManager still needs to be a mock, I just don't want to write all my metadata down again.
I'm using the DoctrineModule for Zend 2 so it would be useful to be able to use my configuration to get the Metadata object, but I assume it's also ok to read the required sections manually.
Example:
public function testGetUniqueFields()
{
$this->prepareGetUniqueFields(); // about 50 lines of mocking ClassMetadata
$entity = 'UniqueWithoutAssociation';
$unique = $this->handler->getUniqueFields($entity);
$expected = ["uniqueColumn"];
$this->assertEquals($expected, $unique,
'getUniqueFields does not return the unique fields');
}
And the code of the actual class:
public function getUniqueFields($class)
{
$unique = array();
$metadata = $this->getClassMetadata($class);
$fields = $metadata->getFieldNames();
foreach ($fields as $field) {
if($metadata->isUniqueField($field) && !$metadata->isIdentifier($field)) {
$unique[] = $field;
}
}
return $unique;
}
The test works like expected, but every time I test another method or another behavior of the method, I need to prepare the mocks again or combine past definitions. Plus, the 50 lines I need for this code are the least I have in this test. Most of the test class is all about the ClassMetadata mock. It is a time consuming and - if you see ClassMetadata as a stable component - unnecessary work.
After spending many hours starring at the Doctrine source code, I found a solution.
Once again, this solution is only if you are working with Doctrines ClassMetadata object so often that it becomes unclean to mock every method call. In every other case you should still create a mock of ClassMetadata.
Still, since the composers minimum stability setting was set to stable, such components can be seen as stable so there is no absolute need to create a mock object.
ClassMetadata depends on several other Doctrine classes, which are all injected through the ubiquitous EntityManager:
Doctrine\ORM\Configuration to get the entity path
Doctrine\Common\Annotations\AnnotationReader and Doctrine\ORM\Mapping\Driver\AnnotationDriver injected through the Configuration object
Doctrine\DBAL\Connection to get the database platform in order to know about the identifier strategy. This object should be mocked so no database calls are possible
Doctrine\DBAL\Platforms\AbstractPlatform as mentioned
Doctrine\Common\EventManager to trigger some events
For single test methods or simple method calls I created a method returning an EntityManager mock object that is capable of returning a valid ClassMetadata object:
/**
* #return EntityManager|\PHPUnit_Framework_MockObject_MockObject
*/
public function getEmMock()
{
$dir = __DIR__."/Asset/Entity/";
$config = Setup::createAnnotationMetadataConfiguration(array($dir), true);
$eventManager = new \Doctrine\Common\EventManager();
$platform = new PostgreSqlPlatform();
$metadataFactory = new ClassMetadataFactory();
$config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader()));
$connectionMock = $this->getMockBuilder('Doctrine\DBAL\Connection')
->disableOriginalConstructor()
->getMock();
$connectionMock->expects($this->any())
->method('getDatabasePlatform')
->will($this->returnValue($platform));
/** #var EntityManager|\PHPUnit_Framework_MockObject_MockObject $emMock */
$emMock = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$metadataFactory->setEntityManager($emMock);
$emMock->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($config));
$emMock->expects($this->any())
->method('getConnection')
->will($this->returnValue($connectionMock));
$emMock->expects($this->any())
->method('getEventManager')
->will($this->returnValue($eventManager));
$emMock->expects($this->any())
->method('getClassMetadata')
->will($this->returnCallback(function($class) use ($metadataFactory){
return $metadataFactory->getMetadataFor($class);
}));
return $emMock;
}
Here you could even manipulate all objects by calling their getters created for the EntityManager mock. But that wouldn't be exactly clean and the method remains inflexible in some cases. Still a simple solution and you could e.g. add some parameters and put the method in a trait to reuse it.
For further needs, I created an abstract class that offers a maximum of flexibility and allows you to mock everything else or create some components in a whole other way.
It needs two configurations: The entity path and the platform object. You can manipulate or replace any object by setting it in the setUp method and then get the required EntityManager mock with getEmMock().
A little bit larger, but here it is:
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Tools\Setup;
/**
* Class AbstractTestWithMetadata
* #author Marius Teller
*/
abstract class AbstractTestWithMetadata extends \PHPUnit_Framework_TestCase
{
const EXCEPTION_NO_ENTITY_PATHS_SET = "At least one entity path must be set";
const EXCEPTION_NO_PLATFORM_SET = "An instance of Doctrine\\DBAL\\Platforms\\AbstractPlatform must be set";
/**
* #var array
*/
protected $entityPaths = [];
/**
* #var AbstractPlatform
*/
protected $platform;
/**
* #var EntityManager
*/
protected $emMock;
/**
* #var Connection
*/
protected $connectionMock;
/**
* #var Configuration
*/
protected $configuration;
/**
* #var EventManager
*/
protected $eventManager;
/**
* #var ClassMetadataFactory
*/
protected $classMetadataFactory;
/**
* #return array
* #throws \Exception
*/
public function getEntityPaths()
{
if($this->entityPaths === []) {
throw new \Exception(self::EXCEPTION_NO_ENTITY_PATHS_SET);
}
return $this->entityPaths;
}
/**
* #param array $entityPaths
*/
public function setEntityPaths(array $entityPaths)
{
$this->entityPaths = $entityPaths;
}
/**
* add an entity path
* #param string $path
*/
public function addEntityPath($path)
{
$this->entityPaths[] = $path;
}
/**
* #return AbstractPlatform
* #throws \Exception
*/
public function getPlatform()
{
if(!isset($this->platform)) {
throw new \Exception(self::EXCEPTION_NO_PLATFORM_SET);
}
return $this->platform;
}
/**
* #param AbstractPlatform $platform
*/
public function setPlatform(AbstractPlatform $platform)
{
$this->platform = $platform;
}
/**
* #return EntityManager
*/
public function getEmMock()
{
if(!isset($this->emMock)) {
/** #var EntityManager|\PHPUnit_Framework_MockObject_MockObject $emMock */
$emMock = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$config = $this->getConfiguration();
$connectionMock = $this->getConnectionMock();
$eventManager = $this->getEventManager();
$classMetadataFactory = $this->getClassMetadataFactory();
$classMetadataFactory->setEntityManager($emMock);
$emMock->expects($this->any())
->method('getConfiguration')
->will($this->returnValue($config));
$emMock->expects($this->any())
->method('getConnection')
->will($this->returnValue($connectionMock));
$emMock->expects($this->any())
->method('getEventManager')
->will($this->returnValue($eventManager));
$emMock->expects($this->any())
->method('getClassMetadata')
->will($this->returnCallback(function($class) use ($classMetadataFactory){
return $classMetadataFactory->getMetadataFor($class);
}));
$this->setEmMock($emMock);
}
return $this->emMock;
}
/**
* #param EntityManager $emMock
*/
public function setEmMock($emMock)
{
$this->emMock = $emMock;
}
/**
* #return Connection
*/
public function getConnectionMock()
{
if(!isset($this->connectionMock)) {
$platform = $this->getPlatform();
/** #var Connection|\PHPUnit_Framework_MockObject_MockObject $connectionMock */
$connectionMock = $this->getMockBuilder('Doctrine\DBAL\Connection')
->disableOriginalConstructor()
->getMock();
$connectionMock->expects($this->any())
->method('getDatabasePlatform')
->will($this->returnValue($platform));
$this->setConnectionMock($connectionMock);
}
return $this->connectionMock;
}
/**
* #param Connection $connectionMock
*/
public function setConnectionMock($connectionMock)
{
$this->connectionMock = $connectionMock;
}
/**
* #return Configuration
*/
public function getConfiguration()
{
if(!isset($this->configuration)) {
$config = Setup::createAnnotationMetadataConfiguration($this->getEntityPaths(), true);
$config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader()));
$this->setConfiguration($config);
}
return $this->configuration;
}
/**
* #param Configuration $configuration
*/
public function setConfiguration(Configuration $configuration)
{
$this->configuration = $configuration;
}
/**
* #return EventManager
*/
public function getEventManager()
{
if(!isset($this->eventManager)) {
$this->setEventManager(new EventManager());
}
return $this->eventManager;
}
/**
* #param EventManager $eventManager
*/
public function setEventManager($eventManager)
{
$this->eventManager = $eventManager;
}
/**
* #return ClassMetadataFactory
*/
public function getClassMetadataFactory()
{
if(!isset($this->classMetadataFactory)) {
$this->setClassMetadataFactory(new ClassMetadataFactory());
}
return $this->classMetadataFactory;
}
/**
* #param ClassMetadataFactory $classMetadataFactory
*/
public function setClassMetadataFactory(ClassMetadataFactory $classMetadataFactory)
{
$this->classMetadataFactory = $classMetadataFactory;
}
}
One more hint: You might have problems with annotations of other classes, e.g. Zend\Form\Annotation\Validator. Such annotations will throw an exception in Doctrines parser because this parser does not use auto loading and only checks for already loaded classes. So if you still want to use them, you just have to include them manually before parsing the classes annotations.
Problem
Hello, I am using Symfony CMF 1.2, liip/imagine-bundle 1.3, symfony-cmf/media-bundle 1.2. I want to add 2 additional image fields to my block that extends ImagineBlock because for every image I upload there will be a mobile and tablet version of the image which is not a simple resize, the aspect ratio or whatnot is not similar. I cannot just crop/resize without affecting the quality of the image.
Attempts
My block
namespace xx\BlockBundle\Document;
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR;
use Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\ImagineBlock;
use Symfony\Cmf\Bundle\MediaBundle\Doctrine\Phpcr\Image;
use Symfony\Cmf\Bundle\MediaBundle\ImageInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class ClickableBlock
* #package xx\BlockBundle\Document
* #PHPCR\Document(referenceable=true)
*/
class ClickableBlock extends ImagineBlock
{
/**
* #PHPCR\Child(nodeName="image-mobile", cascade={"persist"})
* #var Image
*/
protected $imageMobile;
/**
* #PHPCR\Child(nodeName="image-tablet", cascade={"persist"})
* #var Image
*/
protected $imageTablet;
public function setIsPublishable($publishable)
{
$this->setPublishable($publishable);
}
/**
* #return Image
*/
public function getImageMobile()
{
return $this->imageMobile;
}
/**
* #return Image
*/
public function getImageTablet()
{
return $this->imageTablet;
}
/**
* Set the imageMobile for this block.
*
* #param ImageInterface|UploadedFile|null $image optional the imageMobile to update
* #return $this
* #throws \InvalidArgumentException If the $image parameter can not be handled.
*/
public function setImageMobile($image = null)
{
return $this->processImage($image, 'image-mobile', $this->imageMobile);
}
/**
* Set the imageTablet for this block.
*
* #param ImageInterface|UploadedFile|null $image optional the imageTablet to update
* #return $this
* #throws \InvalidArgumentException If the $image parameter can not be handled.
*/
public function setImageTablet($image = null)
{
return $this->processImage($image, 'image-tablet', $this->imageTablet);
}
/**
* #param ImageInterface|UploadedFile|null $image
* #param string $imageName
* #param Image $imageRef
* #return $this
*/
protected function processImage($image, $imageName, $imageRef)
{
if (!$image) {
return $this;
}
if (!$image instanceof ImageInterface && !$image instanceof UploadedFile) {
$type = is_object($image) ? get_class($image) : gettype($image);
throw new \InvalidArgumentException(sprintf(
'Image is not a valid type, "%s" given.',
$type
));
}
if ($imageRef) {
// existing imageTablet, only update content
$imageRef->copyContentFromFile($image);
} elseif ($image instanceof ImageInterface) {
$image->setName($imageName); // ensure document has right name
$imageRef = $image;
} else {
$imageRef = new Image();
$imageRef->copyContentFromFile($image);
}
return $this;
}
}
Admin:
namespace xx\BlockBundle\Admin;
use xx\BlockBundle\Document\ClickableBlock;
use xx\MainBundle\Form\Common\FormMapper as CommonFormMapper;
use Cocur\Slugify\Slugify;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Cmf\Bundle\BlockBundle\Admin\Imagine\ImagineBlockAdmin;
class ClickableBlockAdmin extends ImagineBlockAdmin
{
/**
* {#inheritdoc}
*/
public function toString($object)
{
return $object instanceof ClickableBlock && $object->getLabel()
? $object->getLabel()
: parent::toString($object);
}
/**
* {#inheritdoc}
*/
public function prePersist($document)
{
parent::prePersist($document);
$this->InitialiseDocument($document);
}
/**
* #param $document
*/
private function InitialiseDocument(&$document)
{
$documentManager = $this->getModelManager();
$parentDocument = $documentManager->find(null, '/cms/xx/block');
$document->setParentDocument($parentDocument);
$slugifier = new Slugify();
$document->setName($slugifier->slugify($document->getLabel()));
}
/**
* {#inheritdoc}
*/
public function preUpdate($document)
{
parent::preUpdate($document);
$this->InitialiseDocument($document);
}
/**
* {#inheritdoc}
*/
protected function configureFormFields(FormMapper $formMapper)
{
parent::configureFormFields($formMapper);
if (null === $this->getParentFieldDescription()) {
$imageRequired = ($this->getSubject() && $this->getSubject()->getParentDocument()) ? false : true;
$formMapper
->with('form.group_general')
->remove('parentDocument')
->remove('filter')
->add('parentDocument', 'hidden', ['required' => false, 'data' => 'filler'])
->add('name', 'hidden', ['required' => false, 'data' => 'filler'])
->add('imageMobile', 'cmf_media_image', array('required' => $imageRequired))
->add('imageTablet', 'cmf_media_image', array('required' => $imageRequired))
->end();
// Append common fields to FormMapper
$commonFormMapper = new CommonFormMapper($formMapper);
$formMapper = $commonFormMapper->getPublishingFields();
}
}
}
Note I am unable to inject service container to this class (via constructor/method), that is why am using hardcoded node path and instantiated Slugify class instead of using it's service for now. I am all ears for a solution to this also. Ref -
xx.main.admin.pageadmin.container:
class: xx\MainBundle\Admin\PageAdmin
calls:
- [setContainer,[ #service_container ]]
# arguments: ["#service_container"]
The annotations on the image fields are based on the following config I found in
\vendor\symfony-cmf\block-bundle\Resources\config\doctrine-phpcr\ImagineBlock.phpcr.xml:
<doctrine-mapping
xmlns="http://doctrine-project.org/schemas/phpcr-odm/phpcr-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/phpcr-odm/phpcr-mapping
https://github.com/doctrine/phpcr-odm/raw/master/doctrine-phpcr-odm-mapping.xsd"
>
<document
name="Symfony\Cmf\Bundle\BlockBundle\Doctrine\Phpcr\ImagineBlock"
referenceable="true"
translator="attribute"
>
<node name="node"/>
<locale name="locale"/>
<field name="label" type="string" translated="true" nullable="true"/>
<field name="linkUrl" type="string" translated="true" nullable="true"/>
<field name="filter" type="string" nullable="true"/>
<child name="image" node-name="image">
<cascade>
<cascade-persist/>
</cascade>
</child>
</document>
</doctrine-mapping>
Result
While the default "image" field persists normally, the other two added image fields are not taken into consideration since when I debug on prePersist I see that both fields are null while image field contains its uploaded file.
I tried adding a normal text field which saved and displayed normally on my page.
I use YAML in my project, so I am not sure how exactly the given XML translates, if ever it is the correct mapping to define.
Please help. :)
A colleague found the issue which was the following:
protected function processImage($image, $imageName, $imageRef)
should be
protected function processImage($image, $imageName, &$imageRef)
$imageRef was not passed by reference making it always null. Silly me. Let's hope this code at least helps other people. :)
For the admin question: phpcr-odm admins have a rootPath for exactly the purpose of what you are doing. you could add to your service definition like this:
<call method="setRootPath">
<argument>%cmf_content.persistence.phpcr.content_basepath%</argument>
</call>
and then you do $this->getRootPath()
i need to enable the print order functionality for guest checkout.
for logged in users the print functionality is working fine
but for guest users the print button was not visible on success page
to show print button i have remove the check in file succcess.phtml
<?php if ($this->getCanViewOrder() && $this->getCanPrintOrder()) :?>
<p>
<?php echo $this->__('Click here to print a copy of your order confirmation.', $this->getPrintUrl()) ?>
<?php echo $this->getChildHtml() ?>
</p>
<?php endif;?>
now the print button is visible for guest also but when i click on
that it ask for login.
I need to show the print order for guest also without login.
the print order location is like
localhost/magento1.9/index.php/sales/order/print/order_id/8/
i have check the
/var/www/magento1.9/app/code/core/Mage/Sales/Controller/Abstract.php
and find print action which is as
public function printAction()
{
if (!$this->_loadValidOrder()) {
return;
}
$this->loadLayout('print');
$this->renderLayout();
}
but i cant understand how to make print functionality available for guest without login.
Please suggest me thanks in advance
You should try this extension: https://github.com/eltrino/PrintOrder It works very nicely and is a save way to offere print-links to the order confirmations after a guest checkout.
In Magento 2:
you have to override the module Sales and then overwrite the 2 following Controllers like that:
app/code/YourVendorName/Sales/etc/di.xml
<preference for="Magento\Sales\Controller\Order\PrintAction" type="YourVendorNAme\Sales\Controller\Order\PrintAction"/>
<preference for="Magento\Sales\Controller\AbstractController\OrderLoader" type="YourVendorNAme\Sales\Controller\AbstractController\OrderLoader"/>
app/code/YourVendorName/Sales/Controller/AbstractController/OrderLoader.php
<?php
namespace YourVendorNAme\Sales\Controller\AbstractController;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Controller\Result\Forward;
use Magento\Framework\Controller\Result\Redirect;
use Magento\Framework\Registry;
use Magento\Framework\Controller\Result\ForwardFactory;
use Magento\Framework\Controller\Result\RedirectFactory;
class OrderLoader implements \Magento\Sales\Controller\AbstractController\OrderLoaderInterface
{
/**
* #var \Magento\Sales\Model\OrderFactory
*/
protected $orderFactory;
/**
* #var \Magento\Framework\Registry
*/
protected $registry;
/**
* #var \Magento\Sales\Controller\AbstractController\OrderViewAuthorizationInterface
*/
protected $orderAuthorization;
/**
* #var \Magento\Framework\UrlInterface
*/
protected $url;
/**
* #var ForwardFactory
*/
protected $resultForwardFactory;
/**
* #var RedirectFactory
*/
protected $redirectFactory;
private $checkoutSession;
/**
* #param \Magento\Sales\Model\OrderFactory $orderFactory
* #param OrderViewAuthorizationInterface $orderAuthorization
* #param Registry $registry
* #param \Magento\Framework\UrlInterface $url
* #param ForwardFactory $resultForwardFactory
* #param RedirectFactory $redirectFactory
*/
public function __construct(
\Magento\Sales\Model\OrderFactory $orderFactory,
\Magento\Sales\Controller\AbstractController\OrderViewAuthorizationInterface $orderAuthorization,
Registry $registry,
\Magento\Framework\UrlInterface $url,
ForwardFactory $resultForwardFactory,
RedirectFactory $redirectFactory,
\Magento\Checkout\Model\Session $checkoutSession
) {
$this->orderFactory = $orderFactory;
$this->orderAuthorization = $orderAuthorization;
$this->registry = $registry;
$this->url = $url;
$this->resultForwardFactory = $resultForwardFactory;
$this->redirectFactory = $redirectFactory;
$this->checkoutSession = $checkoutSession;
}
/**
* #param RequestInterface $request
* #return bool|Forward|Redirect
*/
public function load(RequestInterface $request)
{
$orderId = (int)$request->getParam('order_id');
if (!$orderId) {
/** #var Forward $resultForward */
$resultForward = $this->resultForwardFactory->create();
return $resultForward->forward('noroute');
}
$order = $this->orderFactory->create()->load($orderId);
$lastOrderId = $this->checkoutSession->getData('last_order_id');
// Print order if customer is logged in or it's the last order of guest customer
if ($this->orderAuthorization->canView($order) || $lastOrderId == $orderId) {
$this->registry->register('current_order', $order);
return true;
}
/** #var Redirect $resultRedirect */
$resultRedirect = $this->redirectFactory->create();
return $resultRedirect->setUrl($this->url->getUrl('*/*/history'));
}
}
app/code/YourVendorName/Sales/Controller/Order/PrintAction.php
<?php
namespace YourVendorNAme\Sales\Controller\Order;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\View\Result\Page;
use Magento\Sales\Controller\OrderInterface;
class PrintAction extends \Magento\Sales\Controller\AbstractController\PrintAction implements OrderInterface
{
/**
* #return ResponseInterface|ResultInterface|Page
*/
public function execute()
{
$result = $this->orderLoader->load($this->_request);
if ($result instanceof ResultInterface) {
return $result;
}
/** #var Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$resultPage->addHandle('print');
return $resultPage;
}
}
I am trying to access a route and post via a form factory. The route or the post contains an ID which I need to inject into my form so that I can build a select statement.
Currently I am injecting into the form via the controller using
$this->MyForm->get('elementName')->setOptions(array('value_options' =>$myArrayOfOptions));
My goal is to keep the business logic out of the controller hence why I am keen to use the formFactory instead however I do need access to the ID in the post or route to achieve this.
My Form Factory looks like this:
<?php
namespace MyModule\Form;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use AdminLottery\InputFilter\MyFilter;
use AdminLottery\Service\MyService;
class MyFormFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(
ServiceLocatorInterface $serviceLocator
)
{
//$serviceLocator is FormElementManager
$realSL = $serviceLocator->getServiceLocator();
//*** I NEED TO ACCESS THE ID / POST HERE TO SEND TO MY FORM
return new MyForm(
$realSL->get('Doctrine\ORM\EntityManager'),
$realSL->get('InputFilterManager')->get(MyFilter::class),
$realSL,
$realSL->get(MyService::class)
);
}
}
Any Ideas??
You can access the request instance
MyFormFactory
//...
$request = $serviceLocator->getServiceLocator()->get('Request');
$id = $request->getPost('id', false);
if ($id) $form->setOption('id', $id);
//...
Edit: This is very similar to another question I answered
Edit 2
In your factory can access the route params via the router's Zend\Mvc\Router\RouteMatch.
$request = $serviceLocator->getServiceLocator()->get('Request');
$router = $serviceLocator->getServiceLocator()->get('Router');
$match = $router->match($request); // \Zend\Mvc\Router\RouteMatch
$id = ($match) ? $match->getParam('id', false) : false;
if ($id) $form->setOption('id', $id); //....
For anyone looking for a reference, I thought I would add the final code I used:
Semi-final code::
$router = $realSL->get('Router');
$request = $realSL->get('Request');
$routeMatch = $router->match($request);
$matchArray = $routeMatch->getParams();
if (isset($matchArray['id'])) {
$id = (int) $matchArray['id'];
} else {
$id = 0;
}
Final code::
$realSL->get('application')->getMvcEvent()->getRouteMatch()->getParam('id', 0)
Here is an example for a controller factory, that would work in the same manner for any other factory:
namespace MyModule\Controller\Factory;
use MyModule\Controller\MyController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
/*
* #var Zend\ServiceManager\ServiceManager
*/
$realServiceLocator = $serviceLocator->getServiceLocator();
...
$router = $realServiceLocator->get('router');
$request = $realServiceLocator->get('request');
$routerMatch = $router->match($request);
...
$test1 = $routerMatch->getParams();
$test2 = $request->getQuery();
$test3 = $request->getPost();
...
return new MyController(...);
}
}