I'm new in Zend FW 2 and I try to showing data from database in layout but I receive error:
Catchable fatal error: Argument 1 passed to Application\View\Helper\HotNews::__construct() must be an instance of Zend\Db\Adapter\Adapter, none given, called in C:\xampp\htdocs\webtruonghoc\vendor\ZF2\library\Zend\ServiceManager\AbstractPluginManager.php on line 207 and defined in C:\xampp\htdocs\webtruonghoc\module\Application\src\Application\View\Helper\HotNews.php on li
Function getViewHelperConfig in Module.php:
public function getViewHelperConfig()
{
return array(
'factories' => array(
'hotNews' => function($sm) {
$adapter = $sm->getServiceLocator()->get('Application\Model\NewsTable');
return new HotNews($adapter);
},
),
);
}
Add code in module.config.php:
'view_helpers' => array(
'invokables' => array(
'hotnews' => 'Application\View\Helper\HotNews',
),
File HotNews.php:
<?php
namespace Application\View\Helper;
use Zend\Authentication\AuthenticationService;
use Zend\View\Helper\AbstractHelper;
use Zend\Db\Adapter\Adapter;
class HotNews extends AbstractHelper
{
protected $adapter;
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
}
public function __invoke()
{
$sql="SELECT * FROM news order by date DESC limit 0,4";
return $resultSet = $this->adapter->query($sql, \Zend\Db\Adapter\Adapter::QUERY_MODE_EXECUTE);
}
}
and final I showing data in layout:
<?php $hotnews = $this->hotNews();
var_dump($hotnews);
?>
Do I miss something?
It looks like you are expecting your model to be set up as a service. but may not have set up the service correctly. In your module.config.php file, there should be an entry under 'service_manager' => 'factories' :
return array(
'service_manager' => array(
'factories' => array(
'Application\Model\NewsTable' => function (ServiceLocatorInterface $serviceLocator) {
//... returns an instance of Application\Model\NewsTable
}
)
)
);
Your SQL has an error in it. Also, you should not be executing SQL statements inside a view helper, and passing the entire result set of a select * to the view is bad JuJu as well. I would place the SQL inside a Repository class which returns DTO objects representing your data model. You could then inject the repository into your ViewHelper and and use those DTOs in your view.
Related
I have followed an example and would like to pass the Database adapter to a fieldset to create a drop down menu.
The code below is how i call the fieldset.
How can i access the database adapter in the BrandFieldset class?
$this->add(array(
'type' => 'Application\Form\BrandFieldset',
'name' => 'brand',
'options' => array(
'label' => 'Brand of the product',
),
));
Instantiating a fieldset is responsibility of the FormElementManager. When you try to access a form, form element or fieldset, the FormElementManager knows where to find and how to create it. This behaviour summerized in Default Services section of the framework.
Since the proper way of accessing form elements is retrieving them from FormElementManager, I would write a BrandFieldsetFactory to inject that DB adapter or further dependencies to fieldset on construction to achieve this.
A ZF3 friendly fieldset factory would look like:
<?php
namespace Application\Form\Factory;
use Application\Form\BrandFieldset;
use Interop\Container\ContainerInterface;
class BrandFieldsetFactory
{
/**
* #return BrandFieldset
*/
public function __invoke(ContainerInterface $fem, $name, array $options = null)
{
// FormElementManager is child of AbstractPluginManager
// which makes it a ContainerInterface instance
$adapter = $fem->getServiceLocator()->get('Your\Db\Adapter');
return new BrandFieldset($adapter);
}
}
At this point, BrandFieldset should extend the Zend\Form\Fieldset\Fieldset and it's constructor may look like following:
private $dbAdapter;
/**
* {#inheritdoc}
*/
public function __construct(My/Db/Adapter $db, $options = [])
{
$this->dbAdapter = $db;
return parent::__construct('brand-fieldset', $options);
}
Finally, in module.config.php file I'd have a configuration to tell FormElementManager about this factory:
<?php
use Application\Form\BrandFieldset;
use Application\Form\Factory\BrandFieldsetFactory;
return [
// other config
// Configuration for form element manager
'form_elements' => [
'factories' => [
BrandFieldset::class => BrandFieldsetFactory::class
],
],
];
HINT: The BrandFieldset::init() method will be called automatically by FormElementManager after construction. You can put any post-initialization logic into this method.
Based of these docs I was able to find a solution.
https://framework.zend.com/manual/2.1/en/modules/zend.form.advanced-use-of-forms.html
'form_elements' => array(
'invokables' => array(
'fieldset' => BrandFieldsetFactory::class
)
)
I needed to call the form using the service locator in the controller like below.
$sl = $this->getServiceLocator();
$form = $sl->get('FormElementManager')->get('Application\Form\CreateForm');
In addition I changed the __construct to init.
I am creating a website using Zend Framework 2, and I'm using as an example the exercise from the official course of Zend Technology, Zend Framework 2: Fundamentals.
I have a table called posts and I want to show the table content in my home page, ordered by id. These are the codes I have written:
Controller/PostsTableTrait.php
trait PostsTableTrait
{
private $postsTable;
public function setPostsTable($postsTable)
{
$this->postsTable = $postsTable;
}
}
Controller/IndexController.php
class IndexController extends AbstractActionController
{
use PostsTableTrait;
public function indexAction()
{
return new ViewModel(array(
'post' => $this->postsTable->getPosts()
));
}
}
Factory/IndexControllerFactory.php
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$serviceManager = $serviceLocator->getServiceLocator()->get('ServiceManager');
$indexController = new IndexController();
$indexController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
return $indexController;
}
}
Factory/PostsTableFactory.php
class PostsTableFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new PostsTable(PostsTable::$tableName, $serviceLocator->get('Zend\Db\Adapter\AdapterService'));
}
}
Model/PostsTable.php
class PostsTable extends TableGateway
{
public static $tableName = "posts";
public function getPosts()
{
$select = new Select(self::$tableName);
$select->columns(array(
'date',
'title',
'text',
'category'
));
$select->order('id DESC');
return $select;
}
}
config/module.config.php
'controllers' => array(
'invokables' => array(
'Rxe\Controller\Index' => 'Rxe\Controller\IndexController',
'Rxe\Controller\Panel' => 'Rxe\Controller\PanelController'
),
'factories' => array(
'Rxe\Factory\PanelController' => 'Rxe\Factory\PanelControllerFactory'
)
),
'service_manager' => array(
'factories' => array(
'Rxe\Factory\PanelForm' => 'Rxe\Factory\PanelFormFactory',
'Rxe\Factory\PanelFilter' => 'Rxe\Factory\PanelFilterFactory',
'Rxe\Factory\PostsTable' => 'Rxe\Factory\PostsTableFactory',
'Zend\Db\Adapter\AdapterService' => 'Zend\Db\Adapter\AdapterServiceFactory'
)
),
I don't know if the error could be in the getPosts() method. I have tried many different ways to return the query but none of them made any difference, not even showed another error.
You have registered the controller as an 'invokable'. When the the controller manager creates IndexController it will do so without using the IndexControllerFactory; therefore the Rxe\Factory\PostsTable dependency is never set.
To fix this, update module.config.php and register the index controller with your factory class.
'controllers' => [
'factories' => [
'Rxe\Controller\Index' => 'Rxe\Factory\IndexControllerFactory',
],
],
Also (not an error as such) but the IndexControllerFactory calls ->get('ServiceManager') using the service manager.
You could update it to be like this.
class IndexControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllerManager)
{
// #var \Zend\ServiceManager\ServiceManager
$serviceManager = $controllerManager->getServiceLocator();
$indexController = new IndexController();
$indexController->setPostsTable($serviceManager->get('Rxe\Factory\PostsTable'));
return $indexController;
}
}
I was trying to create a plugin controller, like this:
Application\src\Application\Controller\Plugin\Controlador.php
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPluginManager;
use Biblioteca\Mvc\Db\TableGateway;
class Controlador extends AbstractPluginManager
{
protected function getTable($table){
$sm = $this->getServiceLocator();
$dbAdapter = $sm->get('DbAdapter');
$tableGateway = new TableGateway($dbAdapter, $table, new $table);
$tableGateway->initialize();
return $tableGateway;
}
protected function getService($service)
{
return $this->getServiceLocator()->get($service);
}
}
And in my module.config.php i put this:
'controllers' => array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController',
'Controlador' => 'Application\Controller\Plugin\Controlador'
),
),
And in my indexController.php like this:
namespace Application\Controller;</br>
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Biblioteca\ActionController;
class IndexController extends AbstractActionController{
public function indexAction()
{
$controlador = $this->Controlador();
return new ViewModel(array(
'posts' => $controlador->getTable('Application\Model\Post')->fetchAll()->toArray()
));
}
}
and when i execute the code i get this message: "Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for Controlador"
someone can help me ?
You are registering your plugin at the controllerManager which is responsible for creating controller instances. You need to define it at using 'controller_plugins' key in your module config.
return array(
'controller_plugins' => array(
'invokables' => array(
'Controlador' => 'Application\Controller\Plugin\Controlador'
)
)
);
Also you need to inherit AbstractPlugin. Now you are inheriting AbstractPluginManager which you'll use to create your own plugin manager.
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class Controlador extends AbstractPlugin
{
in my Module.php i have the fallowing methods that i would like to move them in a factory class so that i wont clutter the Module class:
public function getControllerConfig()
{
return array(
'factories' => array(
'account-index' => function ($controllerManager) {
$serviceManager = $controllerManager->getServiceLocator();
$accountService = $serviceManager->get('account-service');
return new Controller\IndexController($accountService);
}
)
);
}
public function getServiceConfig()
{
return array(
'factories' => array(
'account-service' => function ($serviceManages) {
return new Service\Account;
}
)
);
}
right now i have:
and where shall i put this factory class, maybe in a Factory folder?
any ideas?
I usually put my factories into ../module/yourmodule/src/yourmodule/Factory.
in your ../module/yourmodule/config/module.config.php you then have to configure your service_manager like so:
'service_manager' => array(
'factories' => array(
'yourfactory' => 'yourmodule\Factory\yourfactory',
),
),
in yourfactory.php You then have to implent the FactoryInterface and set the service locator. Once you done this you should be able to call the service the usual way for controllers, forms etc.
namespace Application\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class yourfactory implements FactoryInterface
{
private $config;
private $serviceLocator;
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $servicelocator->get('Your\Service');
}
After that you can just define functions in your yourfactory.php. In your Controller you call functions like so $serviceManager->get('yourfactory')->yourfunction(yourarguments);
I'm having a problem with setting up a model table from a view helper. I have used the exact same code that I use within my regular controllers: e.g.:
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Application\Model\MenusTable;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
**snipped**
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function getMenusTable()
{
if (!$this->menusTable) {
$sm = $this->getServiceLocator();
$this->menusTable = $sm->get('Application\Model\MenusTable');
}
return $this->menusTable;
}
public function allLinks()
{
$all = $this->getMenusTable()->fetchAll();
return $all;
}
However I am met with this error:
Catchable fatal error: Argument 1 passed to Application\Model\MenusTable::__construct() must be an instance of Zend\Db\Adapter\Adapter, none given, called in C:\xampp\**snipped**\zend\library\Zend\ServiceManager\AbstractPluginManager.php on line 177 and defined in C:\xampp\**snipped**\Application\src\Application\Model\MenusTable.php on line 14
Everything works fine from the main controllers, but here I seem to hit a big problem - I'm new to Zend, but it appears to not be getting the factory from the Module.php file - is there any way to get it?
I have this in my Module.php - as said it works fine in a regular controller, but in a view helper it's not processed for some reason:
public function getServiceConfig()
{
return array
(
'factories' => array
(
'Application\Model\MenusTable' => function($sm)
{
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new MenusTable($dbAdapter);
return $table;
},
),
);
}
After re-reading your question I realized that your using ZF2.
here is a tutorial on using ServiceLocators http://framework.zend.com/wiki/display/ZFDEV2/Proposal+for+ServiceLocator+and+DependencyInjector
From the Documentation you need to define your DB connection.
$services = new ServiceLocator();
// Registering an object:
$services->set('db', $db);
// Lazy-loading by registering a closure:
$services->set('db', function() use ($config) {
$db = Db::factory($config->db);
return $db;
});
// Retrieving:
$db = $services->get('db');
First you should use the getServiceConfig() in your module ::
public function getServiceConfig()
{
return array(
'factories' => array(
'MODULE\Model\MenusTable' => function($sm) {
$tableGateway = $sm->get('MenusTableGateway');
$table = new MenusTable($tableGateway);
return $table;
},
'MenusTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Menus());
return new TableGateway('menus', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
THE ADAPTER might be in your /config/autoload/global.php, like this :
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
// CONNECTION DB
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=YOURDBNAME;host=localhost',
'username' => 'root',
'password' => '',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
Next your View helper must extend AbstractHelper but also implement ServiceLocatorAwareInterface
class MyViewHelper extends AbstractHelper implements ServiceLocatorAwareInterface
I'll put the code in my website