I am integrating Zend Framework and Doctrine 2.
The question is, in my controllers and view, in need to access the model. I can do all this through a single instance of the EntityManager.
Where do I store this instance ?
Zend_Registry ? That's where it is now, it is accessible from everywhere, but not really practical : $em = Zend_Registry::get('EntityManager');
As a controller and view property ? That would be accessible as $this->em, I like this
Create a factory class that will return the instance ? $em = My\EntityManager\Factory::getInstance();. Encapsulation is good, but long to type...
Is the EntityManager a Singleton already ? -> (update) not it is not
I wouldn't recommend using the EntityManager directly in your Controllers and Views. Instead, use a Service layer and inject the EntityManager it that.
I have two custom action helpers, one to retrieve Repositories and one for Services. Each action hold a reference to the EntityManager and inject it accordingly before handing it back to the Controller.
Not my actual code but something like this (not tested):
My/Controller/Action/Helper/Service.php
<?php
namespace My\Controller\Action\Helper;
use Doctrine\ORM\EntityManager;
class Service extends \Zend_Controller_Action_Helper_Abstract
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function direct($serviceClass)
{
return new $serviceClass($this->em);
}
}
You can write a similar Action Helper to retrieve Repositories.
Then, register the helper in your bootstrap (where we also have access to the EntityManager):
<?php
use Zend_Controller_Action_HelperBroker as HelperBroker,
My\Controller\Action\Helper\Service;
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public function _initActionHelpers()
{
$this->bootstrap('doctrine');
$em = $this->getResource('doctrine');
HelperBroker::addHelper(new Service($em));
}
}
Now write a simple Service.
My/Domain/Blog/Service/PostService.php
<?php
namespace My\Domain\Blog\Service;
use Doctrine\ORM\EntityManager,
My\Domain\Blog\Entity\Post;
class PostService implements \My\Domain\Service
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function createPost($data)
{
$post = new Post();
$post->setTitle($data['title']);
$post->setContent($data['content']);
$this->em->persist($post);
$this->em->flush(); // flush now so we can get Post ID
return $post;
}
}
And to bring it all together in a controller action:
<?php
class Blog_PostController extends Zend_Controller_Action
{
private $postService;
public function init()
{
$this->postService = $this->_helper->Service('My\Domain\Blog\PostService');
}
public function createPostAction()
{
// normally we'd get data from the actual request
$data = array(
"title" => "StackOverflow is great!",
"content" => "Imagine where I'd be without SO :)"
);
// and then validate it too!!
$post = $this->postService->createPost($data);
echo $post->getId(); // Blog post should be persisted
}
}
Since the EntityManager is usually created and configured during bootstrap - either as the return value of an explicit _initDoctrine() call or by using an application resource - storing it in the Bootstrap seems to make the most sense to me. Then inside a controller, it is accessible as:
$em = $this->getInvokeArg('bootstrap')->getResource('doctrine');
I see a lot of examples of accessing bootstrap via the front controller singleton:
$em = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('doctrine');
which has the advantage that works everywhere.
Take a look at the integration provided by the Bisna package, written by one of the Doctrine 2 contributes. It is at https://github.com/guilhermeblanco/ZendFramework1-Doctrine2
It allows you to configure Doctrine in your application.ini. It uses an application resource plugin to process the ini settings. I have written documentation for Bisna. It may be integrated into the package by the time you read this. If not, you can find it at https://github.com/kkruecke/ZendFramework1-Doctrine2, in the bisna-documentation/html subdirectory of that package. I have also put the documentation a http://www.kurttest.com/zfa/bisna.html (although this may be temporary).
I store the Entity Manager in Zend_Registry and then I've also created an action helper which I call in my controllers.
Zend_Registry i think is a goods idea.
Basically it is a bad idea to access EM from view, if your really need this feature, your can create a view helper, and use it
Related
What, I had been doing previously was to inject only MY MODELS using the constructor and use Facades for the Laravel's provided classes i.e. Session, Auth, Validator etc, for example. Will it be a good idea if I inject each and every class (either mine or Laravel's) through construct and use it by $this->.. syntax or should I inject my own classes using constructor and use Facades for anything provided by Laravel?
To be more specific, here is what my controllers normally look like:
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark ) {
$this->user = $user;
$this->bookmark = $bookmark
}
public function foobar ( ) {
$user_id = Input::get('bar');
...
Session::get('someInfo');
...
return Redirect::to('/');
}
...
}
Should I structure my methods like controller like following, instead?
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark, Input $input, Session $session, Redirect $redirect ) {
$this->user = $user;
$this->bookmark = $bookmark
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Laravel now supports the same Dependency-Injection functionality for route-related methods of classes (not just constructors), such as controllers and middleware.
You could prevent unnecessary injections by only injecting to methods where the dependency is unique, perhaps leaving more common dependencies in the constructor:
class MyController extends BaseController
{
public function __construct( Input $input, Session $session, Redirect $redirect ) {
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( User $user, Bookmark $bookmark ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Conclusion
As for whether you should do it this way, that's up to you, but consider to:
First, use Dependency-Injection vs Facade (which also enables IDE auto-completion).
Then, wherever dependency is unique (and not required by most routes), use per-method injection vs constructor.
Because making all unique dependencies appear in method definition is easier to unit-test (and seems cleaner to me).
It is elegant and useful to inject certain classes, such as Request. In my opinion they should be specified in controller methods where they are needed, as they are then logically connected to the method implementation. Awesome thus far.
I find two facades to be problemmatic - App and Log. Neither are logically connected to a controller or its actions. App and Log are not inputs in any context. As App and Log are utility classes they are relevant to services and repositories as well, and it gets downright nasty if you type hint them in controllers and then pass them on as constructor or method parameters to your support classes.
An additional issue is that App facade does not implement the Illuminate\Contracts\Auth\Guard interface that it proxies, so my IDE lights up with warnings as static analysis is not possible.
For the sake of consistency and overall separation of concerns I would therefore instantiate both App and Log within a constructor or method, depending on how widespread they are used in a class. To make my IDE happy I created the below class to give me a properly typed instance wherever I need it:
<?php namespace App\Components;
use Illuminate\Contracts\Auth\Guard;
use Psr\Log\LoggerInterface;
/**
* Get the underlying object instances of facades from the container.
*/
class AppGlobal
{
/**
* Returns the global logger instance.
*
* #return LoggerInterface
*/
public static function log()
{
return app('log');
}
/**
* Returns the global auth instance, which internally proxies a guard.
*
* #return Guard
*/
public static function auth()
{
return app('auth');
}
}
If you need an object wit properties - put it in as an injection (e.g Input, Session...), otherwise, if you don't store any data in the object and pretty happy using class, than go with facades (e.g Log::..., Redirect::...).
Laravel has replaced many of it's facade with helpers for example
use Auth;
and
Auth::user()
is now just
auth()->user()
this makes thing simpler and neater (also prevents mistakes)
I would suggest using the helpers where possible and if no helper exists, use the facade because it is easier to mock than an injected instance.
Original Question
I've read every page of the "book" about service containers, and I'm still baffled because things seem to randomly not work nearly every time I try to use $this->container. For example, I'm building a form in my custom bundle controller following the instructions.
My controller extends the base controller as usual:
namespace Gutensite\ArticleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Gutensite\ArticleBundle\Entity\Article;
class AdminEditController extends Controller
{
public function indexAction() {
$content = new Article();
$form = $this->createFormBuilder($content)
->add('content', 'text');
// same issue with the shortcut to the service which I created according the instructions
// $form = $this->createForm('myForm', $myEntity)
//...more code below...
}
}
This produces an error:
Fatal error: Call to a member function get() on a non-object in /vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php on line 176
If we look at that file at that line number we see Symfony's code:
public function createFormBuilder($data = null, array $options = array())
{
return $this->container->get('form.factory')->createBuilder('form', $data, $options);
}
So WHY is symfony's own controller NOT able to access the container->get() function?!
What am I doing wrong?
Along these same lines, I can't figure out why sometimes I can't access the container via $this->container in my own controller (if extend the framework controller or if I reference it by passing it in the construct, etc). It seems random...
Background of Project and Structure of Code
I am building a CMS that has user's routes (URLs) stored in a database. So I have one route defined which directs all requests to my main CMS Controller:
gutensite_cms_furl:
# Match Multiple Paths (the plain / path appears necessary)
path: /
path: /{furl}
defaults: { _controller: GutensiteCmsBundle:Init:index }
# Allow / in friendly urls, through more permissive regex
requirements:
furl: .*
The InitController looks up the requested URL and gets the correct Route entity which points to a View entity that defines which Bundle and Controller to load for specific page type being requested, e.g. the route for /Admin/Article/Edit points to content type that is associated with the Article bundle and AdminEdit controller, which then creates a new object for this content type (Gutensite\ArticleBundle\Controller\AdminEditController.php) and executes the required functions. This then injects the necessary variables back into the main ViewController which gets passed to the template to be rendered out to the page.
This main controller extends symfony controller and I have confirmed that the container is accessible in this controller, e.g. $this->container->get('doctrine') works.
// Gutensite\CmsBundle\Controller\InitController.php
namespace Gutensite\CmsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Gutensite\CmsBundle\Entity;
class InitController extends Controller
{
public function indexAction(Request $request, $furl)
{
// Confirm container is accessible (yes it is)
$test = $this->container->get('doctrine');
// Look up the View Entity based on the Route Friendly URL: $furl
$viewController = $this->container->get('gutensite_cms.view');
$viewController->findView($furl, $siteId);
// Load the Requested Bundle and Controller for this View
$path = $viewController->view->namespace_controller."\\".$viewController->view->controller;
$content = new $path;
// Execute the main function for this content type controller, which adds variables back into the $viewController to be passed to the template.
$content->indexAction($viewController);
return $this->render(
$viewController->view->bundle_shortcut.'::'.$viewController->view->getTemplatesLayout(),
array('view' => $viewController)
);
}
}
FYI, the ViewController is defined as a global service:
services:
gutensite_cms.view:
class: Gutensite\CmsBundle\Controller\ViewController
arguments: [ "#service_container" ]
And then Below is a simplified version of the Gutensite/CmsBundle/Controller/ViewController.php
namespace Gutensite\CmsBundle\Controller;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
class ViewController
{
protected $container;
public $routing;
public $view;
public function __construct(Container $container) {
$this->container = $container;
}
public function findView($furl, $siteId=NULL) {
$em = $this->container->get('doctrine')->getManager();
$this->routing = $em->getRepository('GutensiteCmsBundle:Routing\Routing')->findOneBy(
array('furl'=>$furl, 'siteId'=>$siteId)
);
if(empty($this->routing)) return false;
// If any redirects are set, don't bother getting view
if(!empty($this->routing->getRedirect())) return FALSE;
// If there is not view associated with route
if(empty($this->routing->getView())) return FALSE;
$this->view = $this->routing->getView();
$this->setDefaults();
}
}
Back in the InitController.php we retrieved the view object and loaded the right bundle and controller function. In this case it loaded `Gutensite\ArticleBundle\Controller\AdminEditController.php which is where we lose access to the service container.
namespace Gutensite\ArticleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Gutensite\ArticleBundle\Entity\Article;
class AdminEditController extends Controller
{
protected $request;
public function __contstruct(Request $request) {
$this->request = $request;
}
public function indexAction($view)
{
// TEST: Test if I have access to container (I do not)
//$doctrine = $this->container->get('doctrine');
// This loads createForm() function from the Symfony Controller, but that controller then doesn't have access to container either.
$form = $this->createForm('view', $content);
}
}
More Specific Question
So I ASSUMED that if you extend the Symfony Controller, which itself extends ContainerAware, that the object would be "aware of the container". But that evidently is not the case. And that is what I need to understand better. I assume somehow the container has to be injected manually, but why? And is that the standard method?
Ok. Your assumption that merely making an object ContainerAware will automatically cause the container to be injected is incorrect. The PHP new operator does not know anything about dependencies. It's the job of the dependency injection container to take care of automatically injecting stuff. And of course your are not using the container to create your controllers.
Easy enough to fix:
$path = $viewController->view->namespace_controller."\\".$viewController->view->controller;
$content = new $path;
$content->setContainer($this->container);
$content->indexAction($request,$viewController);
I don't really follow your flow. The view stuff seems kind of backwards to me but I trust you can see where and how the container is injected into a Symfony controller. Don't do anything in the controller's constructor which relies on the container.
===============================================================
Instead of using the new operator, you could use the service container.
$contentServiceId = $viewController->view->contentServiceId;
$content = $this->container->get($contentServiceId);
$content->indexAction($request,$viewController);
Instead of having you view return a class name, have it return a service id. You then configure your controller in services.yml and off you go. This cookbook entry might help a bit: http://symfony.com/doc/current/cookbook/controller/service.html
=============================================================
All ContainerAware does is to make the Symfony DependencyInjectContainer inject the container. Nothing more. Nothing less. You might conside reading through here: http://symfony.com/doc/current/components/dependency_injection/index.html just to get basic idea of what dependency injection and dependency injector container are all about.
I'm using Symfony2.3 and I currently using EntityManager as shown inside __construct()
Which its a better aproach using EntityManager from __construct() or using inside each method ? as shown in public indexAction()
/**
* QuazBar controller.
*
*/
class QuazBarController extends Controller
{
public function __construct()
{
$this->em = $GLOBALS['kernel']->getContainer()->get('doctrine')->getManager();
}
/**
* Lists all QuazBar entities.
*
*/
public function indexAction(Request $request)
{
$session = $request->getSession();
$pagina = $request->query->get('page', 1);
$em = $this->getDoctrine()->getManager();
}
If you must have the EntityManager available in your constructor, a good way to get it is injecting it to the constructor.
To do this you must define your controller as a service.
# src/Acme/DemoBundle/Resources/config/services.yml
parameters:
# ...
acme.controller.quazbar.class: Acme\DemoBundle\Controller\QuazBarController
services:
acme.quazbar.controller:
class: "%acme.controller.quazbar.class%"
# inject doctrine to the constructor as an argument
arguments: [ #doctrine.orm.entity_manager ]
Now all you have to do is modify your controller:
use Doctrine\ORM\EntityManager;
/**
* QuazBar controller.
*
*/
class QuazBarController extends Controller
{
public function __construct(EntityManager $em)
{
$this->em = $em;
}
// ...
}
If you do not require the Entity Manager in the constructor, you can simply get it using the Dependency Injection Container from any method in your controller:
$this->getDoctrine()->getManager();
OR
$this->container->get('doctrine')->getManager();
Controller/setter injection is a good choice because you are not coupling your controller implementation to the DI Container.
At the end which one you use is up to your needs.
In symfony 2.3, I believe that a connection to the doctrine entity manager is built into the controller class.
$em = $this->getDoctrine()->getManager();
Best practice is to make this call in the controllers when you need it. If it's simply convenience, you could derive a controller class and add something like getEm() if you find that too odious.
Often, your own controller class is a good idea, for baking in security restrictions and making your code more DRY.
Can we define a constructor in the controller class ? The doctrine is a service. Does it make any difference to get doctrine in constructor or to get wherever you want want it from di. Both ways you get the same service. Why do you want to inject the em that is already injected.
Read books From Apprentice To Artisan and Implementing Laravel by Chris Fidao and now i don't know how to correctly work with Models in Repositories.
In Implementing laravel book author is working with models in this way:
Example #1
<?php
use MyApp\Interfaces\UserInterface;
use Illuminate\Database\Eloquent\Model;
class UserRepository implements UserInterface
{
protected $user;
public function __construct(Model $user)
{
$this->user = $user;
}
public function find($userId)
{
return $this->user->find($userId);
}
}
But that can by done in other way, not injecting Model as a dependency, like this:
Example #2
Built example using tutorial http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories/
<?php
use MyApp\Interfaces\UserInterface;
use MyApp\Models\User\User;
class UserRepository implements UserInterface
{
public function find($userId)
{
return User::with('profile')->find($userId);
}
}
Why in first example Model is injected, why not use directly Model like in example two?
Which way is correct and why ?
Also which way will be more testable with integrated to laravel UnitTest package ?
The example 2 is bad because it's coupling your repository to a particular implementation of the User model.
Every time you use your repository it'll need to instantiate Univemba\Models\User\User. The whole idea of Dependency Injection is to inject (send) in to your object whatever dependencies it has. If your object needs a Model to work with you can send it a Laravel Eloquent Model, but any of your co-workers could also need to send to it a Doctrine Model. But if you couple your class to Eloquent, this isn't possible.
So in the first example, there is no instantiation happening on your code and it's not using a concrete class directly as in the second:
return User::with('profile')->find($userId);
It is receiving an implementation in the process of its instantiation:
public function __construct(Model $user)
{
$this->user = $user;
}
There are better ways to do that, because it is still expecting a concrete class while it should be expecting an implementation of an interface
public function __construct(ModelInterface $user)
{
$this->user = $user;
}
In this case you just need to pass to your object something that implements ModelInterface, which could be
Univemba\Models\EloquentModel
Or
Univemba\Models\DoctrineModel
Because both would be implementing
Univemba\Models\ModelInterface
I think if a Repository is intended to be the Eloquent implementation of a RepositoryInterface it isn't a bad idea to use the EloquentModel directly.
I have been developing a project in zend 1 but decided to move over to zend 2 to take advantages of things like events etc.
My initial problem is that I can't seem to find any tutorials on how to use models in the way I need to use them.
What I have is an Api controller which is routed to as /api/soap
this soap endpoint loads a class that has all the methods I want to expose via SOAP
namespace MyProject\Controller;
$view = new ViewModel();
$view->setTerminal(true);
$view->setTemplate('index');
$endpoint = new EndpointController();
$server = new Server(
null, array('uri' => 'http://api.infinity-mcm.co.uk/api/soap')
);
$server->setObject($endpoint);
$server->handle();
and my controller that contains all the functions is
namespace MyProject\Controller;
class EndpointController
{
public function addSimpleProducts($products)
{
}
}
Now what I want to be able to do is access my products model from inside this EndpointController.
So i've tried this:
protected function getProductsTable()
{
if (!$this->productsTable) {
$sm = $this->getServiceLocator();
$this->productsTable= $sm->get('MyProject\Model\ProductsTable');
}
return $this->productsTable;
}
When I run this I get the fatal error that EndpointController::getServiceLocator() is undefined.
I am very new to Zend 2 but in Zend 1 it feels like this would be a very minor step in my development and im getting to the point of sacking zend 2 off and going back to zend 1 or even switching to symfony 2 where its simple to use doctrine...
help?
If you want your controller to have access to the ServiceManager, then you need to inject a ServiceManager into it.
Within the MVC system, this happens pretty much automatically for you as the ServiceManager is used to create the instance of the Controller. This is not happening for you as you are creating your EndpointController using new.
You either need to create this controller via the MVC or instantiate and configure your own ServiceManager instance and pass it to EndpointController.
Alternatively, instantiate the dependencies, such as ProductTable and set them into your EndpointController.
To have access to the service locator you have to implement ServiceLocatorAwareInterface
So in any controller that will need this, you can do it like this:
namespace MyProject\Controller;
use Zend\ServiceManager\ServiceLocatorAwareInterface,
Zend\ServiceManager\ServiceLocatorInterface;
class EndpointController implements ServiceLocatorAwareInterface
{
protected $sm;
public function addSimpleProducts($products) {
}
/**
* Set service locator
*
* #param ServiceLocatorInterface $serviceLocator
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
$this->sm = $serviceLocator;
}
/**
* Get service locator
*
* #return ServiceLocatorInterface
*/
public function getServiceLocator() {
return $this->sm;
}
}
Now the service manager will inject itself automagically. You can then use it like:
$someService = $this->sm->getServiceLocator()->get('someService');
If you are using PHP 5.4+ you can import the ServiceLocatorAwareTrait so you don't have to define the getters and setters yourself.
class EndpointController implements ServiceLocatorAwareInterface
{
use Zend\ServiceManager\ServiceLocatorInterface\ServiceLocatorAwareTrait