Also i begin learning zend framework 2. Install skillet application with composel. All goes fine until i didnt begin read that chapter http://framework.zend.com/manual/2.3/en/user-guide/forms-and-actions.html
in part where i try to create new class object in controller /src/Album/controller/AlbumController.php in function addAction().
There my PHP error:
http://i63.fastpic.ru/big/2014/1201/93/9684e5bcc24b52fec546f07e90abe593.png
There my IDE:
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Album\Model\Album;
use Album\Form\AlbumForm; //COLORED RED "Form"
class AlbumController extends AbstractActionController
{
protected $albumTable;
public function indexAction()
{
return new ViewModel(array(
'albums' => $this->getAlbumTable()->fetchAll(),
));
}
public function addAction()
{
$form = new AlbumForm();
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if ($request->isPost()) {
$album = new Album(); //THE EEROR STRING THAT I GET IN PHP.
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$album->exchangeArray($form->getData());
$this->getAlbumTable()->saveAlbum($album);
// Redirect to list of albums
return $this->redirect()->toRoute('album');
}
}
return array('form' => $form);
}
Related
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.
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.
Here's what I have so far,
Form:
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as Hydrator;
class UserRegisterForm extends
Form implements InputFilterProviderInterface
{
public function __construct($entityManager)
{
parent::__construct('Practitioner');
$this
->setAttribute('method', 'post')
->setHydrator(new Hydrator($entityManager), 'App\Entity\Users', true)
->setObject(new Users())
;
// add fields here ...
}
}
Controller:
class UsersController extends AbstractActionController {
public function addAction()
{
$request = $this->getRequest();
$form = new PractitionerRegisterForm($this->getEntityManager());
$users = new \App\Entity\Users;
$form->bind($users);
if ( $request->isPost() ) {
$form->setData($request->getPost());
// echo pre var_dump($this) /pre
$users->getDetails();
} else {
// do something here
}
return ['form' => $form];
}
but what I'm getting from $users->getDetails() is attributes with NULL
So how do I use Hydrator to populate the entity Users?
Solution: I had to validate the form by using $form->isValid() which validates then hydrates(populates) the entity.
When i go to controllers index URL preDispatch is called before entering indexAction method of that controller but postDispatch is not called after it. Why it is not called and should i force it somehow to call postDispatch?
I have two plugin with postDispach methods whitch are not called.
If i got to URL e.g admin/listperms postDispatch is called and everything works.
AdminController.php
class AdminController extends Application_Controllers_Base {
public function indexAction() {
$this->view->headTitle = $this->alias('TITLE_ADMIN_PANEL');
}
public function listpermsAction() {
$this->view->headTitle = $this->alias('TITLE_PERMISSION_LIST');
$permService = new Application_Services_AdminPermission();
$perms = $permService->getPermissionList();
if(!$perms) {
// TODO:
} else {
$this->view->permList = $perms;
}
}
}
Base.php
class Application_Controllers_Base extends Zend_Controller_Action {
}
DbLogPlugin.php
class Application_Plugin_DbLogPlugin extends Zend_Controller_Plugin_Abstract {
public function postDispatch(Zend_Controller_Request_Abstract $request) {
$view = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('view');
$view->developerLog = $this->getQuerysLogs();
}
}
bootstrap.php
protected function _initAutoload(){
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application_', //'
'basePath' => APPLICATION_PATH
));
$autoloader->addResourceType('dao', 'dao', 'Dao');
$autoloader->addResourceType('services', 'services', 'Services');
$autoloader->addResourceType('plugins', 'plugins', 'Plugins');
$autoloader->addResourceType('models', 'models', 'Models');
$autoloader->addResourceType('controllers', 'controllers', 'Controllers');
require_once('controllers/FrontController.php');
$front = Application_Controllers_FrontController::getInstance();
$front->setControllerDirectory(APPLICATION_PATH.'/controllers')
->setRouter(new Zend_Controller_Router_Rewrite())
->registerPlugin(new Application_Plugin_DbLogPlugin())
->registerPlugin(new Application_Plugin_AuthPlugin())
->registerPlugin(new Application_Plugin_ViewPlugin());
return $autoloader;
}
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