ZF3: ServiceNotFoundException while creating a class with Abstract Factory registered in ServiceManager - zend-framework

I got problem with the Abstract Factories example.
I get ServiceNotFoundException while creating a class with Abstract Factory registered in the ServiceManager.
First I download zend-servicemanager with composer
composer require zendframework/zend-servicemanager
Then I run the ServiceManager example in the single file (for simplicity).
<?php
require_once 'vendor/autoload.php';
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\Factory\InvokableFactory;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\ServiceManager\Factory\AbstractFactoryInterface;
Class that should be obtained with ServiceManager.
class A
{
public $text;
public function __construct()
{
$this->text = "Default text";
}
}
I use MyAbstractFactory from documentation.
class MyAbstractFactory implements AbstractFactoryInterface
{
public function canCreate(ContainerInterface $container, $requestedName)
{
return in_array('Traversable', class_implements($requestedName), true);
}
public function __invoke(ContainerInterface $container,
$requestedName,
array $options = null)
{
return $requestedName();
}
}
I create ServiceManager with registered Abstract Factory.
$serviceManager = new ServiceManager([
// Neither works
//'abstract_factories' => array('MyAbstractFactory')
'abstract_factories' => array( new MyAbstractFactory() )
//'abstract_factories' => array( MyAbstractFactory::class )
/*
'abstract_factories' => [
MyAbstractFactory::class => new MyAbstractFactory()
]
*/
]);
Finally I try to obtain the instance of class A.
$a = $serviceManager->get(A::class);
var_dump($a);
I get
Fatal error: Uncaught Zend\ServiceManager\Exception\ServiceNotFoundException: Unable to resolve service "A" to a factory; are you certain you provided it during configuration?
with the stack trace
.\vendor\zendframework\zend-servicemanager\src\ServiceManager.php(763): Zend\ServiceManager\ServiceManager->getFactory('A') #1
.\vendor\zendframework\zend-servicemanager\src\ServiceManager.php(200): Zend\ServiceManager\ServiceManager->doCreate('A') #2
.\script.php(53): Zend\ServiceManager\ServiceManager->get('A') #3

My formulation of the problem was wrong. #rkeet's comment made it clear.
As my objective was the working example of the Abstract Factory registration within ServiceManager, I post the single-file script where canCreate() is simplified just to check if the class exists.
<?php
// composer require zendframework/zend-servicemanager
require_once 'vendor/autoload.php';
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\Factory\AbstractFactoryInterface;
class A
{
public $text;
public function __construct()
{
$this->text = "Default text";
}
}
class MyAbstractFactory implements AbstractFactoryInterface
{
public function canCreate(ContainerInterface $container, $requestedName)
{
return class_exists($requestedName);
}
public function __invoke(ContainerInterface $container,
$requestedName,
array $options = null)
{
return new $requestedName();
}
}
$serviceManager = new ServiceManager([
//'abstract_factories' => array('MyAbstractFactory') // works
//'abstract_factories' => array( new MyAbstractFactory() ) // works
'abstract_factories' => array( MyAbstractFactory::class ) // also works
]);
try {
$a = $serviceManager->get(A::class);
var_dump($a);
echo "<br/>\n";
$b = $serviceManager->get(B::class);
var_dump($b);
} catch (Exception $e) {
echo $e->getMessage() . "<br/>\n";
echo "{$e->getFile()} ({$e->getLine()})";
}
?>
As #rkeet pointed, if someone needs the original example working, class A should implement Traversable.

Related

Omnipay Paypal set store name and logo (Laravel)

How can I set the store name and logo using the Omnipay Paypal package for Laravel?
Please see my code below, until now I tried to use the method setBrandName on the gateway variable, but this method was not found. It seems like the class ExpressCheckout has such methods but is like deprecated and one should use this code and checkout procedure. Also tried to add entry to the purchase method array parameter but it didn't work.
<?php
namespace App\Http\Controllers;
..
use Omnipay\Omnipay;
class PurchaseController extends Controller
{
public $gateway;
public function __construct()
{
$this->gateway = Omnipay::create('PayPal_Rest');
$this->gateway->setClientId(...);
$this->gateway->setSecret(...);
$this->gateway->setTestMode(true);
}
protected function makePurchase(Request $request) {
try {
$items = ...;
$response = $this->gateway->purchase(array(
'amount' => $cart->getSum(),
'items' => $items,
'currency' => config('paypal.currency'),
'returnUrl' => route('payment.success'),
'cancelUrl' => route('cart'),
))->send();
if ($response->isRedirect()) {
$response->redirect(); // this will automatically forward the customer
} else {
// not successful
return $response->getMessage() . ", " . $response->getCode();
}
} catch(Exception $e) {
return $e->getMessage();
}
}
protected function purchaseCompleted(Request $request) {
...
}
}

How to call a function from one module to another Magento 2

I try to call a function from a module A to a module B
here is the module A code
namespace A\Epayment\Model;
class Etransactions
{
public function customPayment{
return "test";
}
and module b code
namespace B\Payment\Controller\Index;
class Payment extends \Magento\Framework\App\Action\Action
{
protected $_pageFactory;
protected $_transaction;
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $pageFactory,
\ETransactions\Epayment\Model\Etransactions $transaction
)
{
$this->_pageFactory = $pageFactory;
$this->_transaction = $transaction;
parent::__construct($context);
}
public function execute()
{
echo "Hello World".PHP_EOL;
$foo="a";
echo $foo;
echo $this->_transaction->customPayment();
//echo $this->customPayment();
echo $foo;
exit;
}
}
this code return the "hello world", the first $foo, not the second and doesn't display any error
can someone explain me where is my error ?
EDIT: i didn't change anything but it works fine now.
thanks for the answers anyway
The object you want create the path your are injecting is incorrect.
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $pageFactory,
\A\Epayment\Model\Etransactions $transaction // changes are here
)
{
$this->_pageFactory = $pageFactory;
$this->_transaction = $transaction;
parent::__construct($context);
}
Kindly use exception handling.
try{
$this->_transaction->customPayment();
}catch(Exception $e){
//log your exception here.
}
In Magento, Helper classes are available to use anywhere (Block, Controller, Model, Observer, View). So you should create a method in helper class and call it anywhere by the following way.
Declar the helper class and method: ModuleA\Epayment\Helper\Data.
<?php
namespace ModuleA\Epayment\Helper;
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
public function yourHelperMethod()
{
# code...
}
}
Call the method:
$helper = $this->_objectManager->create(ModuleA\Epayment\Helper\Data::class);
$helper->yourHelperMethod();
Note: If the object manager is not injected in your class. Please follow the steps below:
1) declare private property:
private $_objectManager;
2) inject in the constructor to initialize:
public function __construct(
\Magento\Framework\ObjectManagerInterface $objectmanager
) {
$this->_objectManager = $objectmanager;
}
3) use in some method:
public function someMethod() {
$helper = $this->_objectManager->create(ModuleA\Epayment\Helper\Data::class);
$helper->yourHelperMethod();
}

Pass a value from controller to partial in ZF2

I want to pass some value from controller to view partial, basically, view partial is set using the placeholder in Application\Module.php, here is the code that does it.
<?php
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\View\Renderer\PhpRenderer;
use Zend\View\Resolver;
use Zend\ServiceManager\ServiceManager;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$eventManager->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this, 'initBreadcrumbAndActionButtonWidget'));
$moduleRouteListener->attach($eventManager);
$serviceManager = $e->getApplication()->getServiceManager();
}
public function initBreadcrumbAndActionButtonWidget(\Zend\Mvc\MvcEvent $e)
{
$serviceManager = $e->getApplication()->getServiceManager();
$config = $serviceManager->get('config');
$viewHelperManager = $this->getViewHelperManager($e->getApplication()->getServiceManager());
$partialHelper = $viewHelperManager->get('partial');
$partial = $partialHelper('widgets/breadcrumb', array(
'config' => $config,
'actionButtons' => $config['action-buttons']
));
$placeHolder = $viewHelperManager->get('placeholder');
$placeHolder('widgets/breadcrumb')->set($partial);
}
public function getViewHelperManager($serviceManager)
{
$stack = new Resolver\TemplatePathStack(array('script_paths' => array(__DIR__ . '/view/')));
$resolver = new Resolver\AggregateResolver();
$resolver->attach($stack);
$renderer = new PhpRenderer();
$renderer->setResolver($resolver);
$viewHelperManager = $serviceManager->get('ViewHelperManager');
return $viewHelperManager;
}
public function getConfig()
{
return array_merge_recursive(
include __DIR__ . '/config/module.config.php',
);
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}
It is all working good till here, however now i want to pass a value from controller to the partial widgets/breadcrumb, after several attempt i thought implementing a ViewHelper could be a solution, and i implemented following view helper.
<?php
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class Variable extends AbstractHelper
{
protected $data;
public function __invoke()
{
return $this;
}
public function set($identifier, $data)
{
$this->data[$identifier] = $data;
return $this;
}
public function get($identifier)
{
return $this->data[$identifier];
}
}
In controller i assign the value to ViewHelper like this.
$viewHelperManager = $this->getServiceLocator()->get('ViewHelperManager');
$variable = $viewHelperManager->get('variable');
$variable->set('stack', 'overflow');
And in partial, to access the value i use the get method.
$this->variable()->get('stack');
Now my problem is that get() is rendered first hence the value set in controller has no effect.
My question is how can i make the set() gets rendered first so i can have the value in partial, or if there is any better approach of passing the value to partial please suggest.
Thanks.
Baaah, i called the view helper in wrong action, it worked when i moved the $variable->set('stack', 'overflow'); in the action where i wanted the value to pass.
Not sure if there can be a better solution, but this does the job for me to pass the value after i found no way in ZF2 that satisfies my requirement.

ZF2 (2.1 method) Populating select/drop down element in form

I used Zend Framework 2.1(not 2.0x) method to populate a select/drop down that is described in following links:
http://zf2.readthedocs.org/en/develop/modules/zend.form.advanced-use-of-forms.html#handling-dependencies
http://www.michaelgallego.fr/blog/2012/11/09/discover-whats-coming-for-zendform-in-zf-2-1/
Though it seems I have done as they told I got a error message like:
*... ::__construct() must be an instance of Zend\Db\TableGateway\TableGateway, none given, called in ...*
which seems service locator is not used properly.
My form code that adds my FieldSet SupplierFieldset:
namespace Inventory\Form;
use Zend\Form\Form;
use Inventory\Model;
class ItemForm extends Form
{
public function init()
{
$this->add(array(
'name' => 'sup_code',
'type' => 'Inventory\Form\SupplierFieldset'
));
}
}
My 'SupplierFieldset' class:
namespace Inventory\Form;
use Inventory\Model;
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\InputFilter\InputFilterProviderInterface;
use Inventory\Model\SupplierTable;
use Inventory\Model\Supplier;
class SupplierFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
protected $supplierTable;
public function init()
{
parent::__construct('Suppliers Code');
$this->setLabel('Supplier Code');
$this->setName('supplier_code');
$suppliers = $this->getSupplierTable()->fetchAll();
$select = new Element\Select('supplier_code');
$options = array();
foreach ($suppliers as $supplier) {
$options[$supplier->id] = $supplier->sup_code;
}
$select->setValueOptions($options);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function getSupplierTable()
{
if (!$this->supplierTable) {
$sm = $this->getServiceLocator();
$this->supplierTable = $sm->get('Inventory\Model\SupplierTable');
}
return $this->supplierTable;
}
}
My Module.php getFormElementConfig() function:
public function getFormElementConfig()
{
return array(
'factories' => array(
'SupplierFieldset' => function($sm) {
$serviceLocator = $sm->getServiceLocator();
$supplierTable = $serviceLocator->get('Inventory\Model\SupplierTable');
$fieldset = new SupplierFieldset($supplierTable);
return $fieldset;
}
)
);
}
My SupplierTable.php model:
namespace Inventory\Model;
use Zend\Db\TableGateway\TableGateway;
class SupplierTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
}
I know SupplierTable model's constructor needs a TableGateway $tableGateway parameter. But this model is working properly when called from SupplierController.

Symfony2: Delete entity through post request (improvements required)

I need some improvements about my actual way to delete entities:
public function deleteAction($path)
{
$form = $this->createFormBuilder(array('path' => $path))
->add('path')
->setReadOnly(true)
->getForm();
if ($this->getRequest()->getMethod() === 'POST') {
$form->bindRequest($this->getRequest());
if ($form->isValid()) {
$image = $this->getImageManager()->findImageByPath($path);
$this->getImageManager()->deleteImage($image);
return $this->redirect($this->generateUrl('AcmeImageBundle_Image_index'));
}
}
return $this->render('AcmeImageBundle:Image:delete.html.twig', array(
'form' => $form->createView(),
));
}
Two improvements I already found while writting:
CreateFormBuilder in extra method in controller
Hidden field and overgive extra image-entity to get rendered
Are there other thing I could make better?
Regards
(my answer is too long for the comment so i add it here)
First you have to create a Type file (generally in YourApp\YourBundle\Form\yourHandler.php), some basique code to put inside if you don't know:
<?php
namespace ***\****Bundle\Form;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\ORM\EntityManager;
use ***\****Bundle\Entity\your_entity;
class *****Handler
{
protected $form;
protected $request;
protected $em;
public function __construct(Form $form, Request $request, EntityManager $em)
{
$this->form = $form;
$this->request = $request;
$this->em = $em;
}
public function process()
{
if( $this->request->getMethod() == 'POST' )
{
$this->form->bindRequest($this->request);
if( $this->form->isValid() )
{
$this->onSuccess($this->form->getData());
return true;
}
}
return false;
}
public function onSuccess(your_entity $object)
{
// Make your stuff here (remove,....)
}
}
And in your controller i just call it this way:
if (!empty($_POST))
{
$formHandler = new *****Handler($my_form, $this->get('request'), $this->getDoctrine()->getEntityManager());
$formHandler->process();
}
Hope i'm clear enough