I am new in Zend 2.
I have made a controller and Model.
I am getting the following error:
Fatal error: Call to a member function get() on a non-object in C:\websites\zend2\module\Pages\src\Pages\Model\PagesTable.php on line 25
What do i do wrong?!?!
SOLUTION:
controller:
namespace Pages\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController {
protected $pagesTable;
function indexAction() {
return new ViewModel(array(
'pages' => $this->getPagesTable()->fetchAll(),
));
}
public function getPagesTable()
{
if (!$this->pagesTable) {
$sm = $this->getServiceLocator();
$this->pagesTable = $sm->get('Pages\Model\PagesTable');
}
return $this->pagesTable;
}
}
Model:
namespace Pages\Model;
use Zend\Db\TableGateway\TableGateway;
class PagesTable {
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
}
Add
Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'Pages\Model\PagesTable' => function($sm) {
$tableGateway = $sm->get('PagesTableGateway');
$table = new PagesTable($tableGateway);
return $table;
},
'PagesTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
return new TableGateway('pages', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
This is because the functin getServiceLocator() is a function that is implemented in the AbstractController which AbstractActionController extends from, which then again you are extending your Controllers from.
The ServiceLocator itself is injected by the ServiceManager.
The way you wanna be doing things is like this:
// SomeController#someAction
$table = $this->getServiceLocator()->get('MyTableGateway');
$pages = $table->pages();
A very clean and slim Controller. Then you set up a Service for MyTableGateway
// Module#getServiceConfig
'factories' => array(
'MyTableGateway' => function($serviceLocator) {
$dbAdapter = $serviceLocator()->get('Zend\Db\Adapter\Adapter');
$gateway = new MyTableGateway($dbAdapter);
return $gateway;
}
)
This Factory will call your class MyTableGateway and then use Constructor-Injection to inject the Dependency, which in this case is Zend\Db\Adapter\Adapter.
All that's left for you to do is to modify the __construct() of your MyTableGateway to allow for the DbAdapter-Parameter and you're done. That way you gain access to the DbAdapter inside your Gateway and the code is all clean and separated ;)
Related
I am getting an error as ;
Argument 1 passed to Album\Controller\AlbumController::__construct() must be an instance of Album\Controller\AlbumTable, instance of Album\Model\AlbumTable given,, called in /var/www/html/zf/module/Album/src/Module.php on line 43
My Module.php is;
<?php
namespace Album;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\ResultSet\ResultSet;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\Db\TableGateway\TableGateway;
class Module implements ConfigProviderInterface
{
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
// Add this method:
public function getServiceConfig()
{
return [
'factories' => [
Model\AlbumTable::class => function($container) {
$tableGateway = $container->get(Model\AlbumTableGateway::class);
return new Model\AlbumTable($tableGateway);
},
Model\AlbumTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Album());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
],
];
}
public function getControllerConfig()
{
return [
'factories' => [
Controller\AlbumController::class => function($container) {
return new Controller\AlbumController(
$container->get(Model\AlbumTable::class)
);
},
],
];
}
}
My AlbumController is like;
<?php
namespace Album\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Album\Model;
class AlbumController extends AbstractActionController
{
// Add this property:
private $table;
// Add this constructor:
public function __construct(AlbumTable $table)
{
$this->table = $table;
}
public function indexAction()
{
return new ViewModel([
'albums' => $this->table->fetchAll(),
]);
}
public function addAction()
{
}
public function editAction()
{
}
public function deleteAction()
{
}
}
Can you please tell me what I am doing wrong? I am very new in Zend Framework. It is the tutorial application I am trying to run.I followed all steps but there were a lot of issues and I solved all those one by one , now I am stuck here.
you are using illegal dependency so to speak,
// Here you are returning Model\AlbumTable object
// while your Controller\AlbumController needs a Controller\AlbumTable instance
return new Controller\AlbumController(
$container->get(Model\AlbumTable::class)
);
so to solve this :
return new Controller\AlbumController(
$container->get(Controller\AlbumTable::class)
);
in case if you need to use Model\AlbumTable as a dependency, so you will need to set it in your controller as follows :
use Model\AlbumTable as AlbumTableModel;
...
...
public function __construct(AlbumTableModel $table)
{
$this->table = $table;
}
Add the AlbumTable class in AlbumController.php.
use Album\Model\AlbumTable;
hello am new to phpunit test and am stuck here.
I've followed this tutorial: Zend Framework 2 : Centralize phpunit test
After that i created a module test
namespace ModulesTests\ServiceProvidersTest\Model;
use PHPUnit_Framework_TestCase;
use ModulesTests\ServiceManagerGrabber;
use User\Service\ServiceProvider;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
class TestServiceProviders extends PHPUnit_Framework_TestCase
{
protected $serviceManager;
protected $serviceprovider;
public function setUp()
{
$serviceManagerGrabber = new ServiceManagerGrabber();
$this->serviceManager = $serviceManagerGrabber->getServiceManager();
$this->serviceprovider = new ServiceProvider() ;
}
public function testSPdetails()
{
$stack = array('1','2');
$this->serviceprovider->getDetails($stack);
}
}
In my ServiceProvider class
namespace User\Service;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
class ServiceProvider implements ServiceLocatorAwareInterface
{
use ServiceLocatorAwareTrait;
public function getModel()
{
$em = $this->getServiceLocator()- >get('doctrine.entitymanager.orm_default');
return $em->getRepository('User\Entity\ServiceProvider');
}
public function getDetails($data = null,$fields='*')
{
$where = 1;
$company_ids = implode(',',$data);
if(isset($company_ids)){
$where = 'sp.id IN('.$company_ids.')';
}
if(isset($fields)){
}
$db = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter');
$query = 'some query';
.....Rest code.......
}
}
}
am getting this error :
Call to a member function get() on null in /opt/lampp/htdocs/project/module/User/src/User/Service/ServiceProvider.php
Please help what am missing here..??
So there are a few things I notice here:
1: You have not shown any code showing how you have hooked up your service to the service manager, so it is unclear if this will ever work
2: You directly instantiate your class when you need to be using the service manager grabber you have written
$this->serviceprovider = new ServiceProvider() ;
becomes
$serviceManagerGrabber = new ServiceManagerGrabber();
$this->serviceManager = $serviceManagerGrabber->getServiceManager();
$this->serviceprovider = $this->serviceManager->get('YOUR_SERVICE_KEY');
3: You probably should start with unit tests not these module integration tests being explained in that article. see https://framework.zend.com/manual/2.3/en/modules/zend.test.phpunit.html
4: The ServiceLocatorAwareInterface is deprecated you should probably use a factory and the factories key of service manager config to inject your dependencies
5: Your code seems to mix doctrine and zend db I don't know why you've done this, but my suggestion is ... it's be a bad idea
Here is an example of how you might put this together:
module.config.php
<?php
namespace Application;
return [
'service_manager' => [
'factories' => [
'ServiceProvider' => function ($serviceManager) {
// This shouldn't be in this anon function, it should be its own
// factory but I'm lazy and already writing loads of code for this example
// #see https://framework.zend.com/manual/2.4/en/in-depth-guide/services-and-servicemanager.html#writing-a-factory-class
$service = new \Application\Service\ServiceProvider(
$serviceManager->get('doctrine.entitymanager.orm_default')
);
return $service;
},
]
],
];
ServiceProvider.php
<?php
namespace Application\Service;
use Doctrine\ORM\EntityManager;
class ServiceProvider
{
protected $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function getModel()
{
return $this->entityManager- >getRepository('User\Entity\ServiceProvider');
}
public function getDetails($data = null, $fields='*')
{
$serviceProviderRepository = $this->entityManager->getRepository('User\Entity\ServiceProvider');
return $data;
}
}
ModuleTest
<?php
namespace ModulesTests\Application\Service;
use PHPUnit_Framework_TestCase;
use ModulesTests\ServiceManagerGrabber;
class ServiceProvidersTest extends PHPUnit_Framework_TestCase
{
protected $serviceManager;
protected $serviceprovider;
public function setUp()
{
$serviceManagerGrabber = new ServiceManagerGrabber();
$this->serviceManager = $serviceManagerGrabber->getServiceManager();
$this->serviceprovider = $this->serviceManager->get('ServiceProvider');
}
public function testSPdetails()
{
$stack = array('1','2');
$this->serviceprovider->getDetails($stack);
}
}
unit test:
<?php
namespace ModulesTests\Application\Service;
use PHPUnit_Framework_TestCase;
use Application\Service\ServiceProvider;
use Prophecy\Argument;
class ServiceProvidersUnitTest extends PHPUnit_Framework_TestCase
{
protected $entityManager;
protected $serviceprovider;
public function setUp()
{
$this->entityManager = $this->prophesize("Doctrine\\ORM\\EntityManager");
$this->entityManager->getRepository(Argument::exact('User\Entity\ServiceProvider'))
->willReturn(true);
$this->serviceprovider = new ServiceProvider($this->entityManager->reveal());
}
public function testSPdetails()
{
$stack = array('1','2');
$this->serviceprovider->getDetails($stack);
$this->entityManager->getRepository(Argument::exact('User\Entity\ServiceProvider'))
->shouldHaveBeenCalledTimes(1);
}
}
My Controller :
<?php
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Admin\Controller;
use Admin\Service;
class AdminController extends AbstractActionController
{
public function indexAction()
{
$CrudService = $this->getServiceLocator()->get('CrudService');
return new ViewModel(
array('list'=> $CrudService->getList())
);
}
}
Service Layer :
<?php
namespace Admin\Service;
use Admin\Dao;
class CrudService
{
public function getList()
{
$CrudDao=new Dao\CrudDao();
$list=$CrudDao->getList();
return $list;
}
}
Dao Layer :
<?php
namespace Admin\Dao;
class CrudDao
{
public function getList()
{
return
$this->getServiceLocator()->
get('doctrine.entitymanager.orm_default')->
getRepository('Admin\Entity\ProductEntity')
->findAll();
}
}
every things is good work But My Problem is Dao Layer. that give me This
Error : not Found get Service Locator Class
I want get data From Doctrine in Dao Layer and Call Dao Method in Service Layer And Next Call Service With getServiceLocator in Controller
You have to inject all dependencies and use the service manager to get the classes.
In you Module.php you have to register and inject the dependencies:
class Module
{
// ...
public function getServiceConfig()
{
$factories = [
'Admin\Dao\CrudDao' = function (ServiceManager $serviceManager) {
$entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'),
return new CrudDao($entityManager);
},
'Admin\Service\CrudService' = function (ServiceManager $serviceManager) {
return new CrudService($serviceManager);
}
];
return $factories;
}
}
The Dao will receive the EntityManager:
<?php
namespace Admin\Dao;
class CrudDao
{
private $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function getList()
{
return
$this->entityManager
->getRepository('Admin\Entity\ProductEntity')
->findAll();
}
}
Your CrudService will receive the Service manager, then you can get the CrudDao:
<?php
namespace Admin\Service;
use Admin\Dao;
class CrudService
{
public function __construct($serviceManager)
{
$this->serviceManager = $serviceManager;
}
public function getList()
{
$CrudDao= $this->serviceManager->get('Admin\Dao\CrudDao');
$list = $CrudDao->getList();
return $list;
}
}
And your controller:
<?php
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Admin\Controller;
use Admin\Service;
class AdminController extends AbstractActionController
{
public function indexAction()
{
$CrudService = $this->getServiceLocator()->get('Admin\Service\CrudService');
return new ViewModel(
array('list'=> $CrudService->getList())
);
}
}
I started topic on codereview and got nice answer. This answer is here.
All tips are looking nice and now I am trying to follow them in my code.
Author of this answer said, that I am getting object manager in all actions (somewhere 2 times). He suggested to do it in init() method of my controller and assign object manager to $this->objectManager. I tried this:
In that case, that there are no init() method in zf2, I used init:
public function __construct() {
$this->objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
On other actions I am using this:
public function listAction() {
$news = $this->objectManager
->getRepository('\News\Entity\Item')
->findBy(array(), array('created' => 'DESC'));
// some more code
}
When I am trying load the page I am getting this:
Fatal error: Call to a member function get() on a non-object in F:\Server\domains\zf2-skeleton\module\News\src\News\Controller\NewsController.php on line 12
Line 12 is a line in __construct:
$this->objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
In old version I was using similar:
public function indexAction() {
$objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
// how I use $objectManager?
$news = $objectManager
->getRepository('\News\Entity\Item')
->findBy($options, array('created'=>'DESC'));
// some more code
}
Why approach with __construct is not working? What I am doing wrong?
Files on the github:
Old and monstrous controller code, but working one
New and pretty controller code, but not working :(
Update
File MODULE/src/MODULE/Factory/NewsControllerFactory.php contents:
<?
namespace News\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use News\Controller\NewsController;
class NewsControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
var_dump("bob");
$objectManager = $serviceLocator->getServiceLocator()->get('Doctrine\ORM\EntityManager');
return new NewsController($objectManager);
}
}
I tried to var_dump(); something there and realized, that createService function is not called during execution. Why?
This is because the service locator has not been set yet. When you're referencing it in your constructor, the $this->getServiceLocator() is still returning null.
If you want to setup objectManager for use in your controller, I'd recommend using a factory. It makes it easier to test your objects;
class NewsControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$objectManager = $serviceLocator->getServiceLocator()->get('Doctrine\ORM\EntityManager');
return new NewsController($objectManager);
}
}
and then your controller would look like this
class NewsController extends AbstractActionController
{
protected $objectManager;
public function __construct($objectManager)
{
$this->objectManager = $objectManager;
}
}
and the necessary addition to your module.config.php
'controllers' => array(
'factories' => array(
'News\Controller\News' => 'News\Factory\NewsControllerFactory',
)
)
This should work -
use Zend\Mvc\Controller\AbstractActionController;
use Zend\EventManager\EventManagerInterface;
class IndexController extends AbstractActionController {
protected $objectManager;
public function setEventManager(EventManagerInterface $events) {
parent::setEventManager($events);
$this->objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
[......]
}`
Then just use $this->objectManager in any actions or simple functions in that whole controller -
public function listAction() {
$news = $this->objectManager
->getRepository('\News\Entity\Item')
->findBy(array(), array('created' => 'DESC'));
// some more code
}
I am trying to create a simple service in zf2 which I can access using in viewhelper
Step1. I have craeted a class in src/Application/Service/Service1.php as follow
namespace Application\Service;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Service1 implements ServiceLocatorAwareInterface
{
public function __construct()
{
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
}
public function getServiceLocator()
{
}
}
Step2 I set this up in module.php file as below.
public function getServiceConfig()
{
return array(
'factories' => array(
'Application\Service\Service1' => function ($sm) {
return new \Application\Service\Service1($sm);
},
)
);
}
public function onBootstrap($e)
{
$serviceManager = $e->getApplication()->getServiceManager();
$serviceManager->get('viewhelpermanager')->setFactory('Abc', function ($sm) use ($e) {
return new \Application\View\Helper\Abc($sm);
});
}
Step3 finally I am geting it in my view helper src/Application/View/Helper/Abc.php test() method like this, I I comment this line $this->sm->get('Application\Service\Service1'); there is no error, there must be something which I am missing in service?
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class Abc extends AbstractHelper
{
protected $sm;
public function test()
{
$this->sm->get('Application\Service\Service1');
}
public function __construct($sm) {
$this->sm = $sm;
}
}
Step4 then I am calling my test view helper in one of view like this.
$this->Abc()->test();
I am getting following error.
Fatal error: Call to undefined method Application\Service\Service1::setView() in vendor/zendframework/zendframework/library/Zend/View/HelperPluginManager.php on line 127 Call Stack:
what am I missing?
An alternative, in PHP 5.4 only, without specific configuration, would be to use traits:
extract of module.config.php:
'view_helpers' => array(
'invokables' => array(
'myHelper' => 'Application\View\Helper\MyHelper',
),
MyHelper.php:
<?php
namespace Application\View\Helper;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
class HeadScript extends \Zend\View\Helper\MyHelper implements ServiceLocatorAwareInterface
{
use \Zend\ServiceManager\ServiceLocatorAwareTrait;
public function __invoke()
{
$config = $this->getServiceLocator()->getServiceLocator()->get('Config');
// do something with retrived config
}
}
change the line $this->sm->getServiceLocator()->get('Application\Service\Service1'); in below method
class Abc extends AbstractHelper
{
protected $sm;
public function test()
{
$this->sm->getServiceLocator()->get('Application\Service\Service1');
}
public function __construct($sm) {
$this->sm = $sm;
}
}