I had integrated doctrine 2 in codeigniter 2. i had the database which i convert into entity in store in models/Entity
my controller goes like this ..
public function __construct() {
parent::__construct();
$this->em = $this->doctrine->em;
$this->load->model('doctrine_model');
}
public function index()
{
$this->doctrine_model->get_object();
}
}
and model goes like this....
Class Doctrine_model extends CI_Controller
{
function __construct() {
parent::__construct();
//$this->load->library('doctrine');
//$accountTable = Doctrine_Core::getTable('ss_class');
$this->em = $this->doctrine->em;
}
function get_object()
{
$records = $this->em->getRepository("Entity\SsClass")->findAll();
}
}
when i run this code i get this error
Fatal error: Cannot redeclare class SsClass in D:\xampp\htdocs\new_doctrine\application\models\Entity\SsClass.php on line 14
As i had already loaded the doctrine library in autoload but cannot figure out what is going on here...
getRepository method load your entity automatically so probably your entity class is autoload in other place such as config.php or require function.
Related
I want to make my controller thin and to separate business-logic from other operations. For example I have an action:
public function indexAction()
{
$languages = $this ->getEntityManager()
->getRepository('\ApanelLanguage\Entity\LanguageCommon')
->getLanguagesList();
$viewModel = new ViewModel(['languages' => $languages]);
return $viewModel;
}
but I want to get action like this:
public function indexAction()
{
$model = $new LanguageModel();
$model->getLanguagesList();
return $viewModel;
}
Is it possible to do? What must I have in Language/Model/LanguageModel ?
Thank you
Removing the business logic from your controller is a great idea for code reuse and maintainability; however I would recommend against moving the logic to your models. A better solution would be to add a service layer to your application.
What is a service layer? Martin Fowler describes it as the following:
[A service layer] defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.
This essentially means that we add a class in-between your controller and your model.
The great advantage of this approach is that should you need to update the business logic of your application there is no need to update the controller. The controller also becomes unaware of any specific code and therefore can be reusable in other unrelated projects.
This 'service' could have a simple API, for example:
interface ServiceInterface
{
public function setObjectManager($objectManager);
public function setRepository($respository);
public function find($id);
public function fetchRow($criteria);
public function fetchAll($criteria);
public function insert($object);
public function update($object);
public function delete($object);
}
Then you can implement this interface for your new 'LanguageService'.
class LanguageService implements ServiceInterface
{
// ... all methods from interface
public function getLanguageList()
{
return $this->repository->getLanguagesList();
}
}
Lastly update your controller to use the new service
class FooController extends AbstractActionController
{
protected $languageService;
public function __construct(ServiceInterface $languageService)
{
$this->languageService = $languageService;
}
public function indexAction()
{
$languages = $this->languageService->getLanguageList();
$viewModel = new ViewModel(['languages' => $languages]);
return $viewModel;
}
public function insertAction()
{
$request = $this->getRequest();
$service = $this->languageService;
$form = $service->getInsertForm();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
// if our form used the DoctrineObjectHydrator
// we will get a entity back populated with the
// form data
$language = $service->insert($form->getData());
if ($language instanceof Entity\Language) {
// success
} else {
// failure
}
}
}
//
}
}
I am trying to set up the simplest of tests in my controller but, as with most things Laravel, there are no decent tutorials to demonstrate the simple stuff.
I can run a simple test (in a file called UserControllerTest) like this:
public function testIndex()
{
$this->call('GET', 'users');
$this->assertViewHas('users');
}
This calls the /users route and passes in an array users.
I want to do the same with Mockery but how?
If I try this:
public function testIndex()
{
$this->mock->shouldReceive('users')->once();
$this->call('GET', 'users');
}
I get an error that "Static method Mockery_0_users::all does not exist on this mock object.
Why not? I am mocking User which extends Ardent and in turn extends Eloquent. Why does ::all not exist for the mock?
BTW, these are the set-up functions for Mockery:
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('User');
}
public function mock($class)
{
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
You can't directly mock an Eloquent class. Eloquent is not a Facade and your User model neither. There is a bit of magic in Laravel but you can't do things like that.
If you want to mock your User class, you have to inject it in the controller constructor. The repository pattern is a good approach if you want to do that. There is a lot of articles about this pattern and Laravel on Google.
Here some pieces of code to show you how it could look like :
class UserController extends BaseController {
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
public function index()
{
$users = $this->users->all();
return View::make('user.index', compact('users'));
}
}
class UserControllerTest extends TestCase
{
public function testIndex()
{
$repository = m::mock('UserRepositoryInterface');
$repository->shouldReceive('all')->andReturn(new Collection(array(new User, new User)));
App::instance('UserRepositoryInterface', $repository);
$this->call('GET', 'users');
}
}
If it seems to be too much structuration for your project you can just call a real database in your tests and don't mock your model classes... In a classic project, it just works fine.
This function is part of a project called apiato.io you can use it to mock any class in Laravel, even facade, basically anything that can be resolved with the IoC, which is almost all classes if you are using proper dependency injection:
/**
* Mocking helper
*
* #param $class
*
* #return \Mockery\MockInterface
*/
public function mock($class)
{
$mock = Mockery::mock($class);
App::instance($class, $mock);
return $mock;
}
I follwed the following tutorial to understand how Doctrine works: http://doctrine-orm.readthedocs.org/en/latest/tutorials/getting-started.html
I now have a better understanding of Doctrine. However I am struggling to understand the entity manager and how to use it.
In the tutorial to get an instance of the entity manager, all you do is this:
$entityManager = \Doctrine\ORM\EntityManager::create($conn, $config);
That is loaded in the bootstrap and is available through out the code base.
So my question is, how is this instantiated in ZF2?
EDIT: I have had some help on this via ZF Talk and the following was recommended to me. I am still however struggling to get it to work:
My goal is to pull an array of users from my database and output them to my view using Doctrines pagination class.
In my Controller for the given action I have:
public function usersAction() {
$userFunctions = $this->getServiceLocator()->get('Administration\Model\UserFunctionFactory');
$userArray = $userFunctions->getUsers();
$viewModel = new ViewModel(array('users' => $userArray));
return $viewModel;
}
The users function Model is as such:
namespace Administration\Model;
use Doctrine\ORM\Tools\Pagination\Paginator;
class UserFunctions
{
protected $em;
function __constructor(EntityManagerInterface $em) {
$this->em = $em;
}
public function getUsers()
{
$em = $this->em;
$dql = "SELECT * FROM Users";
$query = $em->createQuery($dql)
->setFirstResult(0)
->setMaxResults(100);
$paginator = new Paginator($query, $fetchJoinCollection = true);
$c = count($paginator);
foreach ($paginator as $post) {
$myArray[] = $post->getHeadline() . "\n";
}
return $myArray;
}
}
The factory for the SM:
<?php
namespace Administration\UserFunctionFactory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Administration\Model\UserFunctions;
class UserFunctionsFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
return new UserFunctions($em);
}
}
The module.config:
'service_manager' => array(
'factories' => array(
'Administration\Model\UserFunctionFactory' => 'Administration\Model\UserFunctionFactory')
),
I am getting the following error:
While attempting to create administrationmodeluserfunctionfactory(alias: Administration\Model\UserFunctionFactory) an invalid factory was registered for this instance type.
Your immediate issue (based on your code snippets, and the resulting error about the factory being invalid) is actually trivial.
Your module.config.php says the factory class is:
Administration\Model\UserFunctionFactory,
but the fully-qualified class name of the class defined in your factory's classfile is:
Administration\UserFunctionFactory\UserFunctionFactory
The namespace mismatch means the SM can't find your factory. So, your first fix is to make sure your factory is indeed defined in a file like Administration/src/Administration/Model/UserFunctionsFactory.php (assuming your module is using PSR-0), and change the first line to read namespace Administration/Model
this is what basically you should never do in a ZF2 controller
$userFunctions = new UserFunctions();
Instead you create a service (your UserFunctions) and get it in your controller using the service locator
namespace Administration\Service;
use Zend\ServiceManager\FactoryInterface,
Zend\ServiceManager\ServiceLocatorInterface;
use Administration\Model\UserFunctions;
class UserFunctionsFactory
implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new UserFunctions($this->getServiceLocator()->get('Doctrine\ORM\EntityManager'));
}
}
So you user functions class constructor will be
use Doctrine\ORM\EntityManagerInterface;
// class declaration and props here
function __constructor(EntityManagerInterface $entityManager) {
$this->em = $entityManager;
}
Then you register the service
// file services.config.php
namespace Administration;
return array(
'factories' => array(
'admin.service.contact' => new Service\UserFunctionsFactory()
),
);
Please note that you can do injection using initializers more than use class constructor. I used this method in the above example for simplicity.
This is an example of an initializer that inject DoctrineEntityManager
namespace My\Service\Initializer;
use Zend\ServiceManager\InitializerInterface,
Zend\ServiceManager\ServiceLocatorInterface,
Zend\Debug\Debug;
use My\Service\EntityManagerAwareInterface;
class EntityManagerAwareInitializer
implements InitializerInterface
{
public function initialize($instance, ServiceLocatorInterface $serviceLocator)
{
if($instance instanceof EntityManagerAwareInterface) {
$instance->setEntityManager($serviceLocator->get('doctrine.entitymanager'));
}
}
}
and the interface
namespace My\Service;
use Doctrine\ORM\EntityManagerInterface;
interface EntityManagerAwareInterface
{
/**
* Set Doctrine 2 Entity Manager
*
* #param EntityManagerInterface $entityManager
*/
function setEntityManager(EntityManagerInterface $entityManager);
/**
* Get Doctrine 2 Entity Manager
*/
function getEntityManager();
}
and the registration of the initializer
'initializers' => array(
'My\Service\Initializer\EntityManagerAwareInitializer' => new EntityManagerAwareInitializer()
),
Please note that I have separated configuration files because my module implements some interfaces that gives the possibility to do so, for example
namespace My;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface,
Zend\ModuleManager\Feature\ControllerProviderInterface,
Zend\ModuleManager\Feature\ServiceProviderInterface,
Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module
implements
AutoloaderProviderInterface,
ControllerProviderInterface,
ServiceProviderInterface,
ConfigProviderInterface
{
public function getConfig()
{
return include __DIR__ . '/../../config/module.config.php';
}
public function getControllerConfig()
{
return include __DIR__ . '/../../config/controllers.config.php';
}
public function getServiceConfig()
{
return include __DIR__ . '/../../config/services.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/../../src/' . __NAMESPACE__,
),
),
);
}
}
My Module.php is inside my src, so pay attention to paths.
Hope it helped!
I had a simple controller action
class CatalogController extends AbstractActionController {
public function indexAction() {
return new ViewModel();
}
// ...
}
and a unit test for it:
class CatalogControllerTest extends AbstractHttpControllerTestCase
{
public function testIndexActionCanBeAccessed()
{
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
$this->assertInstanceOf('Zend\View\Model\ViewModel', $result);
}
It worked fine.
Now I'm forwarding the request
public function indexAction() {
return $this->forward()->dispatch('Catalog/Controller/Catalog', array('action' => 'list-cities'));
}
and getting an error by unit testing after $this->controller->dispatch($this->request);:
PHP Fatal error: Call to a member function getEventManager() on a non-object in /var/www/path/to/project/vendor/zendframework/zendframework/library/Zend/Mvc/Controller/Plugin/Forward.php on line 147
How do you / how should one test action methods with forwards?
Thx
Have you tried dispatching like this? I have just tried forwarding inside one of my controller actions and unit tests work fine. This is my code:
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase
class IndexControllerTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
require APPLICATION_PATH . '/init_autoloader.php';
$testConfig = include APPLICATION_PATH . '/config/test.php';
$this->setApplicationConfig($testConfig);
parent::setUp();
}
public function testFoo()
{
$this->dispatch('/catalogue');
$this->assertResponseStatusCode(200);
$this->assertModuleName('Catalogue');
$this->assertControllerName('Catalogue\Controller\Index');
$this->assertControllerClass('IndexController');
$this->assertActionName('index');
$this->assertMatchedRouteName('logcataloguen');
}
}
I want to make some action (php script) before all actions in my frontend app and then pass a result from that script to actions in variable - so I can get variable value from all actions. Where should I declare sth like this?
If the filter solution dont feet your needs, you can also create a base action class with a preExecute function:
// app/frontend/lib/baseActions.class.php
class baseActions extends sfActions
{
public function preExecute()
{
$this->myVar = .... // define your vars...
}
}
Then your module actions class extends your baseActions class:
// app/frontend/modules/myModule/actions/actions.class.php
class myModuleActions extends baseActions
{
public function executeIndex(sfWebRequest $request)
{
// var $this->myVar is available in any action and in your template
...
}
}
if you have to use the preExecute function in your module class action, remember to call parent::preExecute() in it.
What kind of information ?
I would recommend you to use filters.
In your apps/frontend/config/filters.yml:
rendering: ~
myfilter:
class: myCustomFilter
Create the file lib/filter/myCustomFilter.php:
<?php
class myCustomFilter extends sfFilter
{
public function execute ($filterChain)
{
if ($this->isFirstCall())
{
// do what ever you want here.
$config = Doctrine_Core::getTable('Config')->findAll();
sfConfig::set('my_config', $config);
}
$filterChain->execute();
}
}
And then, every where, you can retrieve your data:
sfConfig::get('my_config');