How to add resource for module in Zend? - php

I have a test module.
I have a class in myproject/application/modules/test/lists/Profiles.php
class Test_List_Profiles {
// class members
}
Now when I access this class in myproject/application/modules/test/controllers/ProfileController.php
public function indexAction() {
$profilesList = new Test_List_Profiles();
}
It gives me following error:
Fatal error: Class 'Test_List_Profiles' not found
I have following entry in Bootstrap.php as well:
protected function _initAutoload() {
$autoLoader = Zend_Loader_Autoloader::getInstance();
$testModuleLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH . 'modules/test',
'namespace' => 'Test_',
'resourceTypes' => array( 'form' => array( 'path'=>'forms/', 'namespace'=>'Form_' ),
'list' => array( 'path'=>'lists/', 'namespace'=>'List_' )
)
));
}
How to access Test_List_Profiles class everywhere in project ?
Thanks

You should be able to add your list resource in the test module bootstrap class (myproject/application/modules/test/Bootstrap.php) this way:
class Test_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initAutoload(){
$autoloader = $this->getResourceLoader();
$autoloader->addResourceType('list', 'models/lists', 'Model_List');
return $autoloader;
}
}

There was error in basePath path. A slash(/) was missing.
protected function _initAutoload() {
$autoLoader = Zend_Loader_Autoloader::getInstance();
$testModuleLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH . '/modules/test',
'namespace' => 'Test_',
'resourceTypes' => array( 'form' => array( 'path'=>'forms/', 'namespace'=>'Form_' ),
'list' => array( 'path'=>'lists/', 'namespace'=>'List_' )
)
));
}
It is working now.

Related

EDIT: Class Zend\Db\Adapter\Adapter not found

I have a simple form which after submitting redirects to processAction inside AuthController and in this action I want to create a simple table bar.
EDITED:
Referring to Zend framerwork DB DDL update, I made a little modification in below code
AuthController.php
<?php
namespace Blog\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Debug\Debug;
use Blog\Form\LoginForm;
use Zend\Authentication\AuthenticationService;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\Sql\Sql;
use Zend\Db\Sql\Ddl;
use Zend\Db\Sql\Ddl\Column;
use Zend\Db\Sql\Insert;
use Zend\Authentication\Adapter\DbTable as DbTableAuthAdapter;
class AuthController extends AbstractActionController
{
protected $adapter;
public function getAdapter()
{
if (!$this->adapter) {
$sm = $this->getServiceLocator();
$this->adapter = $sm->get('Zend\Db\Adapter\Adapter');
}
return $this->adapter;
}
public function indexAction()
{
return new ViewModel();
}
public function processAction()
{
$DB = new \Zend\Db\Adapter\Adapter(array(
'driver' => 'Pdo',
'database' => 'blog',
'username' => 'root',
'password' => 'mysql'
));
$this->adapter = $this->getAdapter();
$sql = new Sql($this->adapter);
$table = new Ddl\CreateTable('bar');
$table->addColumn(new Column\Integer('id'));
$table->addColumn(new Column\Varchar('name', 255));
$table->setTable('bar');
$results = $this->adapter->query($sql->getSqlStringForSqlObject($table), $this->adapter ::QUERY_MODE_EXECUTE);
return new ViewModel();
}
}
global.php:
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=blog;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter'
=> 'Zend\Db\Adapter\AdapterServiceFactory',
),
),
);
Module.php
<?php
namespace Blog;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module implements AutoloaderProviderInterface,ConfigProviderInterface
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getServiceConfig()
{
return array(
'factories' =>array(
'Zend\Db\Adapter\Adapter' => function ($sm) {
$config = $sm->get('Config');
return new \Zend\Db\Adapter\Adapter($config['db']);
}
)
);
}
}
Problem:(Updated)
table bar is not created and shows error like
Fatal error: Class 'Blog\Controller\Zend\Db\Adapter\Adapter' not found
in /var/www/zend2/module/Blog/src/Blog/Controller/AuthController.php
on line 110
if I print
echo $sql->getSqlStringForSqlObject($table);
The query prints like this
CREATE TABLE `bar` ( `id` INTEGER NOT NULL, `name` VARCHAR(255) NOT NULL )
but table was not there.
Error occurs in below line of code as it is not able to identify adapter :
$results = $this->adapter->query($sql->getSqlStringForSqlObject($table), $this->adapter ::QUERY_MODE_EXECUTE);
But works in this way:
$results = $this->adapter->query($sql->getSqlStringForSqlObject($table), $DB ::QUERY_MODE_EXECUTE);
I am using Zend 2.4
I think you are trying to access non existing service. You can try to create a service factory:
config.php
<?php
return [
'db' => [
'driver' => 'Pdo',
'database' => 'blog',
'username' => 'root',
'password' => 'mysql'
]
];
Module.php
<?php
class Module
{
public function getServiceConfig()
{
return [
'factories' => [
'Zend\Db\Adapter\Adapter' => => function ($sm) {
$config = $sm->get('Config');
return new \Zend\Db\Adapter\Adapter($config['db']);
},
],
];
}
}
Then, you can access the service with the service manager:
AuthController.php
class AuthController extends AbstractActionController
{
public function getAdapter()
{
if (!$this->adapter) {
$sm = $this->getServiceLocator();
$this->adapter = $sm->get('Zend\Db\Adapter\Adapter');
}
return $this->adapter;
}
public function processAction()
{
$this->adapter = $this->getAdapter();
$sql = new Sql($this->adapter);
// other stuff here
}
}
You can find more examples here and here.
For beginners in ZF2 ,the query execute won't work if you don't instantiate class Zend\Db\Adapter\Adapter correctly.
I corrected like
$DB = new \Zend\Db\Adapter\Adapter(array(
'driver' => 'Pdo',
'database' => 'blog',
'username' => 'root',
'password' => 'mysql'
));
Also below line of code :
$results = $this->adapter->query($sql->getSqlStringForSqlObject($table), $DB ::QUERY_MODE_EXECUTE);
Ref Fatal error : class not found in Zend framework 2
Note: I still fail to understand why $this->adapter = $this->getAdapter(); not working instead of $DB.Any hint will be appreciated.

Database Translations Zend Framework 2

I have problem with creating custom translator from database in ZF2. I have a DB like this
and files:
1)Application/module.config.php
'service_manager' => array(
'abstract_factories' => array(),
'factories' => array(
'translator' => function (\Zend\ServiceManager\ServiceManager $serviceManager)
{
$pluginManager = new \Zend\I18n\Translator\LoaderPluginManager();
$pluginManager->setServiceLocator($serviceManager);
$pluginManager->setFactory('DatabaseTranslationLoaderFactory', function($serviceManager)
{
$translator = new \Zend\I18n\Translator\DatabaseTranslationLoaderFactory();
return $translator->createService($serviceManager);
});
$translator = new \Zend\I18n\Translator\Translator(array());
$translator->setFallbackLocale('en_US');
$translator->setPluginManager($pluginManager);
$translator->addRemoteTranslations('DatabaseTranslationLoaderFactory');
return $translator;
},
),
),
'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
array(
'type' => 'Zend\I18n\Translator\Loader\Database',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
2) Zend/I18n/Translator/Loader/Database.php
<?php
namespace Zend\I18n\Translator\Loader;
use Zend\Db\Adapter\Adapter;
use Zend\Db\Sql\Sql;
use Zend\I18n\Translator\Plural\Rule as PluralRule;
use Zend\I18n\Translator\TextDomain;
class Database implements RemoteLoaderInterface {
protected $dbAdapter;
public $dbAdapter;
public function __construct(Adapter $dbAdapter = null)
{
if ($dbAdapter === null)
{
$configArray = array('driver' => 'Pdo_Mysql',
'database' => 'dbname',
'username' => 'username',
'password' => 'pswd',
'hostname' => 'localhost',
'charset' => 'utf-8',
);
$dbAdapter = new Adapter($configArray);
}
$this->dbAdapter = $dbAdapter;
}
public function load($locale, $textDomain)
{
$sql = new Sql($this->dbAdapter);
$select = $sql->select('ic_var')->columns(array('value'))
->where(array('language' => $locale, 'name' => $textDomain));
$messages = $this->dbAdapter->query(
$sql->getSqlStringForSqlObject($select),
Adapter::QUERY_MODE_EXECUTE
);
$textDomain = new TextDomain();
foreach ($messages as $message) {
if (isset($textDomain[$message['name']])) {
if (!is_array($textDomain[$message['name']])) {
$textDomain[$message['name']] = array(
$message['plural_index'] => $textDomain[$message['name']]
);
}
$textDomain[$message['name']][$message['plural_index']] = $message['value'];
} else {
$textDomain[$message['name']] = $message['value'];
}
}
return $textDomain;
}
}
3) Zend/I18n/Translator/DatabaseTranslationLoaderFactory.php
<?php
namespace Zend\I18n\Translator;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\I18n\Translator\Loader\Database;
class DatabaseTranslationLoaderFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return new Database($serviceLocator->get('Zend\Db\Adapter\Adapter'));
}
}
4) Application/Module.php
public function onBootstrap(MvcEvent $e)
{
$translator = $e->getApplication()->getServiceManager()->get('translator');
$translator->addTranslationFile(
'DatabaseTranslationLoader',
'text-domain',
'text-domain'
);
}
But translation doesn`t work, because db adapter not find in loader:
Fatal error: Uncaught exception 'Zend\I18n\Exception\RuntimeException' with message 'Specified loader is not a file loader'
Thanks for your answers!
First of all you shouldn't define your custom classes in the Zend namespace as this is reserved a namespace for the ZF2 library and you don't want to touch (or add) files in the vendor directory.
Just put the custom classes in your own namespace outside the vendor folder. i.e. MyI18n
You can register you custom remote loader to the pluginManager in module.config.php.
return array(
'translator' => array(
'loaderpluginmanager' => array(
'factories' => array(
'database' => 'MyI18n\Translator\DatabaseTranslationLoaderFactory',
)
),
'remote_translation' => array(
array(
'type' => 'database' //This sets the database loader for the default textDomain
),
),
)
);
You don't have to overwrite the Translator factory if you want to add a custom loader, so just remove that code in your Module.php.
Als remove the configuration under translation_file_patterns as this is only needed for file loaders.
EDIT
For the above to work you need to overwrite the TranslatorServiceFactory because ZF has no support to register custom loaders on the plugin manager.
namespace MyNamespace\Translator;
use Zend\Mvc\Service\TranslatorServiceFactory as BaseTranslatorFactory;
class TranslatorServiceFactory extends BaseTranslatorFactory
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return MvcTranslator
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$translator = parent::createService($serviceLocator);
$config = $serviceLocator->get('Config');
$pluginManagerConfig = isset($config['translator']['loaderpluginmanager']) ? $config['translator']['loaderpluginmanager'] : array();
$pluginManager = new LoaderPluginManager(new Config($pluginManagerConfig));
$pluginManager->setServiceLocator($serviceLocator);
$translator->setPluginManager($pluginManager);
return $translator;
}
}
Now register your custom factory in the service configuration:
class Module
{
public function getServiceConfig()
{
return array(
'factories' => array(
'MvcTranslator' => 'MyNamespace\Translator\TranslatorServiceFactory',
)
)
}
}
I register custom remote loader to the pluginManager in module.config.php like this
'translator' => [
'loaderpluginmanager' => [
'factories' => [
'database' => function($lpm){
$sm = $lpm->getServiceLocator();
$loader = new Zf2Translation\Loader\DatabaseTranslationLoader($sm);
return $loader;
},
],
],
'remote_translation' => [
[
'type' => 'database',
],
],
]
Next in Database Loader class
use Zend\I18n\Translator\Loader\RemoteLoaderInterface;
class DatabaseTranslationLoader implements RemoteLoaderInterface
{
protected $dbAdapter;
protected $sm;
public function __construct(ServiceManager $sm)
{
$this->sm = $sm;
$this->dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
}
}
I hope it helps.

ZF2 tutorial Album PHPUnit test failing with Undefined property: AlbumTest\Model\AlbumTableTest::$controller

I am building the ZF2 Album tutorial application and when I unit test I am consistently receiving the same error even though I've rebuilt the application again. Can someone tell me what is going on here? I am dumping all relevant information here to assist. The error is:
PHPUnit 3.7.10 by Sebastian Bergmann.
Configuration read from D:\PHP\zf2-tutorial\module\Album\test\phpunit.xml.dist
......E
Time: 0 seconds, Memory: 6.25Mb
There was 1 error:
1) AlbumTest\Model\AlbumTableTest::testGetAlbumTableReturnsAnInstanceOfAlbumTable
Undefined property: AlbumTest\Model\AlbumTableTest::$controller
D:\PHP\zf2-tutorial\module\Album\test\AlbumTest\Model\AlbumTableTest.php:116
FAILURES!
Tests: 7, Assertions: 9, Errors: 1.
AlbumTableTest.php is as follows and the error is received on the final assertion:
<?php
namespace AlbumTest\Model;
use Album\Model\AlbumTable;
use Album\Model\Album;
use Zend\Db\ResultSet\ResultSet;
use PHPUnit_Framework_TestCase;
class AlbumTableTest extends PHPUnit_Framework_TestCase {
public function testFetchAllReturnsAllAlbums() {
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('select'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with()
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
$this->assertSame($resultSet, $albumTable->fetchAll());
}
public function testCanRetrieveAnAlbumByItsId() {
$album = new Album();
$album->exchangeArray(array('id' => 123,
'artist' => 'The Military Wives',
'title' => 'In My Dreams'));
$resultSet = new ResultSet();
$resultSet->setArrayObjectPrototype(new Album());
$resultSet->initialize(array($album));
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('select'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with(array('id' => 123))
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
$this->assertSame($album, $albumTable->getAlbum(123));
}
public function testCanDeleteAnAlbumByItsId() {
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('delete'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('delete')
->with(array('id' => 123));
$albumTable = new AlbumTable($mockTableGateway);
$albumTable->deleteAlbum(123);
}
public function testSaveAlbumWillInsertNewAlbumsIfTheyDontAlreadyHaveAnId() {
$albumData = array('artist' => 'The Military Wives', 'title' => 'In My Dreams');
$album = new Album();
$album->exchangeArray($albumData);
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('insert'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('insert')
->with($albumData);
$albumTable = new AlbumTable($mockTableGateway);
$albumTable->saveAlbum($album);
}
public function testSaveAlbumWillUpdateExistingAlbumsIfTheyAlreadyHaveAnId() {
$albumData = array('id' => 123, 'artist' => 'The Military Wives', 'title' => 'In My Dreams');
$album = new Album();
$album->exchangeArray($albumData);
$resultSet = new ResultSet();
$resultSet->setArrayObjectPrototype(new Album());
$resultSet->initialize(array($album));
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('select', 'update'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with(array('id' => 123))
->will($this->returnValue($resultSet));
$mockTableGateway->expects($this->once())
->method('update')
->with(array('artist' => 'The Military Wives', 'title' => 'In My Dreams'), array('id' => 123));
$albumTable = new AlbumTable($mockTableGateway);
$albumTable->saveAlbum($album);
}
public function testExceptionIsThrownWhenGettingNonexistentAlbum() {
$resultSet = new ResultSet();
$resultSet->setArrayObjectPrototype(new Album());
$resultSet->initialize(array());
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array('select'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with(array('id' => 123))
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
try {
$albumTable->getAlbum(123);
} catch (\Exception $e) {
$this->assertSame('Could not find row 123', $e->getMessage());
return;
}
$this->fail('Expected exception was not thrown');
}
public function testGetAlbumTableReturnsAnInstanceOfAlbumTable() {
$this->assertInstanceOf('Album\Model\AlbumTable', $this->controller->getAlbumTable());
}
}
?>
getAlbumTable is in AlbumController as follows:
<?php
namespace Album\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AlbumController extends AbstractActionController {
protected $albumTable;
public function indexAction() {
return new ViewModel(array(
'albums' => $this->getAlbumTable()->fetchAll(),
));
}
public function addAction() {
}
public function editAction() {
}
public function deleteAction() {
}
public function getAlbumTable() {
if (!$this->albumTable) {
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album\Model\AlbumTable');
}
return $this->albumTable;
}
}
?>
And AlbumTable is:
<?php
namespace Album\Model;
use Zend\Db\TableGateway\TableGateway;
class AlbumTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getAlbum($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
public function saveAlbum(Album $album)
{
$data = array(
'artist' => $album->artist,
'title' => $album->title,
);
$id = (int)$album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteAlbum($id)
{
$this->tableGateway->delete(array('id' => $id));
}
}
?>
Module.php is:
<?php
namespace Album;
class Module
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
// Add this method:
public function getServiceConfig()
{
return array(
'factories' => array(
'Album\Model\AlbumTable' => function($sm) {
$tableGateway = $sm->get('AlbumTableGateway');
$table = new AlbumTable($tableGateway);
return $table;
},
'AlbumTableGateway' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Album());
return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
},
),
);
}
}
?>
module.config.php is:
<?php
return array(
'controllers' => array(
'invokables' => array(
'Album\Controller\Album' => 'Album\Controller\AlbumController',
),
),
// The following section is new and should be added to your file
'router' => array(
'routes' => array(
'album' => array(
'type' => 'segment',
'options' => array(
'route' => '/album[/:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Album\Controller\Album',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'album' => __DIR__ . '/../view',
),
),
);
?>
application.config.php is:
<?php
return array(
'modules' => array(
'Application',
'Album', // <-- Add this line
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'./vendor',
),
),
);
?>
Bootstrap.php:
<?php
namespace AlbumTest;//Change this namespace for your test
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use Zend\Stdlib\ArrayUtils;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
class Bootstrap
{
protected static $serviceManager;
protected static $config;
protected static $bootstrap;
public static function init()
{
// Load the user-defined test configuration file, if it exists; otherwise, load
if (is_readable(__DIR__ . '/TestConfig.php')) {
$testConfig = include __DIR__ . '/TestConfig.php';
} else {
$testConfig = include __DIR__ . '/TestConfig.php.dist';
}
$zf2ModulePaths = array();
if (isset($testConfig['module_listener_options']['module_paths'])) {
$modulePaths = $testConfig['module_listener_options']['module_paths'];
foreach ($modulePaths as $modulePath) {
if (($path = static::findParentPath($modulePath)) ) {
$zf2ModulePaths[] = $path;
}
}
}
$zf2ModulePaths = implode(PATH_SEPARATOR, $zf2ModulePaths) . PATH_SEPARATOR;
$zf2ModulePaths .= getenv('ZF2_MODULES_TEST_PATHS') ?: (defined('ZF2_MODULES_TEST_PATHS') ? ZF2_MODULES_TEST_PATHS : '');
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$baseConfig = array(
'module_listener_options' => array(
'module_paths' => explode(PATH_SEPARATOR, $zf2ModulePaths),
),
);
$config = ArrayUtils::merge($baseConfig, $testConfig);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
static::$config = $config;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
public static function getConfig()
{
return static::$config;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
$zf2Path = getenv('ZF2_PATH') ?: (defined('ZF2_PATH') ? ZF2_PATH : (is_dir($vendorPath . '/ZF2/library') ? $vendorPath . '/ZF2/library' : false));
if (!$zf2Path) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
}
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) return false;
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
?>
TestConfig.php.dist:
<?php
return array(
'modules' => array(
'Album',
),
'module_listener_options' => array(
'config_glob_paths' => array(
'../../../config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'module',
'vendor',
),
),
);
?>
And, finally, phpunit.xml.dist:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="Bootstrap.php">
<testsuites>
<testsuite name="zf2tutorial">
<directory>./AlbumTest</directory>
</testsuite>
</testsuites>
</phpunit>
You are correct to assume that you need to add some setup code and comparing it with AlbumControllerTest.php is a good idea.
As the error message states, the issue is that the AlbumTableTest object does not have controller property. We therefore need to add the property using:
$protected controller
and initialise it using:
$this->controller = new AlbumController()
In addition, we need to initialise the serviceManager property and set its serviceLocator property so that the following calls make sense in the getAlbumTable method of the controller:
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album\Model\AlbumTable');
To summarise, you did the right thing but you didn't need all the additional code. You can get away with:
use AlbumTest\Bootstrap;
use Album\Controller\AlbumController;
use Album\Model\AlbumTable;
use Album\Model\Album;
use Zend\Db\ResultSet\ResultSet;
use PHPUnit_Framework_TestCase;
class AlbumTableTest extends PHPUnit_Framework_TestCase
{
protected $controller;
protected function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new AlbumController();
$this->controller->setServiceLocator($serviceManager);
}
....
at the start of AlbumTableTest.php
While I am not certain that this is the appropriate fix at this point, I resolved the error. I looked into AlbumControllerTest as opposed to AlbumTableTest. It has a setup method that creates the controller class. I copied the setup code along with the appropriate use statements and variable declarations and, for the moment, I am on to the next issue...
I'm still interested in a better answer!
What I copied (added) includes:
use AlbumTest\Bootstrap;
use Album\Controller\AlbumController;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
use PHPUnit_Framework_TestCase;
class AlbumTableTest extends PHPUnit_Framework_TestCase {
protected $controller;
protected $request;
protected $response;
protected $routeMatch;
protected $event;
protected function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new AlbumController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
}
You forgot to add the new import statements in Module.php. The start of the file should look like this:
namespace Album;
use Album\Model\Album;
use Album\Model\AlbumTable;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\TableGateway\TableGateway;
class Module
{
//...
Easy to skip, hard to find, I've spent good 15 minutes on the same thing...

Using a constant inside a static array

Im on php 5.2.x
I have a loader class that looks like so:
class Loader {
static $account_nameClasses = array(
'DB' => '/home/account_name/public_html/includes/php/DB.php',
'Skin' => '/home/account_name/public_html/includes/php/Skin.php',
'API' => '/home/account_name/public_html/api/apiClass.php',
'Search' => '/home/account_name/public_html/includes/php/Search.php',
'User' => '/home/account_name/public_html/includes/php/User.php'
);
static function loader($className) {
$filename = self::$account_nameClasses[$className];
if(file_exists($filename)){
require_once $filename;
}else{
return false;
}
}
}
spl_autoload_register(array(account_name_Loader, 'loader'));
As you can see, I repeat /home/account_name/public_html/includes/php/ over and over.
I want to replace that with a constant (or anything else) so that I can easily change it if I move the location of the app.
I tried this (Any may forms of this), but it just doesn't work
const PHP_CLASSES_ROOT = '/home/account_name/www/';
static $account_nameClasses = array(
'DB' => self::PHP_CLASSES_ROOT.'DB.php',
'Skin' => self::PHP_CLASSES_ROOT.'Skin.php',
'Search' => self::PHP_CLASSES_ROOT.'Search.php',
'User' => self::PHP_CLASSES_ROOT.'User.php'
);
Any ideas?
You can modify the loader function:
class Loader {
const PHP_CLASSES_ROOT = '/home/account_name/public_html/includes/php/';
static $account_nameClasses = array(
'DB' => 'DB.php',
'Skin' => 'Skin.php',
'API' => 'apiClass.php',
'Search' => 'Search.php',
'User' => 'User.php'
);
static function loader($className) {
$filename = Loader::PHP_CLASSES_ROOT.self::$account_nameClasses[$className];
if(file_exists($filename)){
require_once $filename;
}else{
return false;
}
}
}
spl_autoload_register(array(account_name_Loader, 'loader'));
Of if the class names are used elsewhere you can create a function to build paths:
class Loader {
const PHP_CLASSES_ROOT = '/home/account_name/public_html/includes/php/';
static $account_nameClasses = array(
'DB' => 'DB.php',
'Skin' => 'Skin.php',
'API' => 'apiClass.php',
'Search' => 'Search.php',
'User' => 'User.php'
);
static function resolveClassPath($className) {
return Loader::PHP_CLASSES_ROOT . $filename;
}
static function loader($className) {
$filename = self::resolveClassPath($className);
if(file_exists($filename)){
require_once $filename;
}else{
return false;
}
}
}
spl_autoload_register(array(account_name_Loader, 'loader'));
You can change your loader function to prepend this constant to $filename
static function loader($className) {
$filename = self::PHP_CLASSES_ROOT . self::$account_nameClasses[$className];
/* ... */
}
When you call a static property, it's already assigned but has no idea PHP_CLASSES_ROOT exists because its a property (cannot be invoked) and not a method.
class Loader {
const PHP_CLASSES_ROOT = '/home/bildr/www/';
static public function account_nameClasses() {
return array(
'DB' => self::PHP_CLASSES_ROOT.'DB.php',
'Skin' => self::PHP_CLASSES_ROOT.'Skin.php',
'Search' => self::PHP_CLASSES_ROOT.'Search.php',
'User' => self::PHP_CLASSES_ROOT.'User.php'
);
}
//...
}
print_r(Loader::account_nameClasses());

Working with multilanguage routers in Zend

I'm developing a multilanguage Zend application with two modules (admin and public), I wanna pass the language code in the url so, in my bootstrap I have:
protected function _initAutoload() {
$this->bootstrap('frontController');
$this->_front = $this->getResource('frontController');
$autoLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => '',
'resourceTypes' => array(
'form' => array(
'path' => 'admin/forms/',
'namespace' => 'Admin_Form_',
),
'model' => array(
'path' => 'models/',
'namespace' => 'Model_'
)
)
));
$autoLoader_ = new Zend_Application_Module_Autoloader(array(
'basePath' => APPLICATION_PATH . '/public/',
'namespace' => 'Public_',
'resourceTypes' => array(
'forms' => array(
'path' => 'forms/',
'namespace' => 'Public_Form_'
)
)
));
return $autoLoader;
}
protected function _initConfig() {
$config = new Zend_Config_Xml(BASE_PATH . '/config.xml', APPLICATION_ENV);
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
return $config;
}
protected function _initDb() {
$this->bootstrap('config');
$config = $this->getResource('config');
$db = Zend_Db::factory($config->database->adapter, $config->database);
$db->setFetchMode(Zend_Db::FETCH_OBJ);
$db->query("SET NAMES 'utf8';");
$db->query("SET CHARACTER SET 'utf8';");
Zend_Db_Table::setDefaultAdapter($db);
return $db;
}
protected function _initRoutes() {
$router = $this->_front->getRouter();
$router->removeDefaultRoutes();
$language = new Zend_Controller_Router_Route(':language', array('language' => 'es'));
$module = new Zend_Controller_Router_Route_Module(
array(
'module' => 'public',
'controller' => 'index',
'action' => 'index'
),
$this->_front->getDispatcher(),
$this->_front->getRequest()
);
$module->isAbstract(true);
$default = new Zend_Controller_Router_Route_Chain();
$default->chain($language);
$default->chain($module);
$router->addRoute('default', $default);
}
http://domain.com/es/admin/ => works
http://domain.com/admin/ => does not work
http://domain.com/es/ => works
http://domain.com/ => does not work
The thing is that I wanna it to work even when I do not specify the language. how can I do it? how can I extract the language code (en) for use it in _initLocale?
Thanks in advance
Here is my bootstrap init for routing. It contains all solutions for working with multilanguage routes and haveing modules and url halper working:
public function _initRoutes() {
$this->bootstrap('FrontController');
$this->_frontController = $this->getResource('FrontController');
$router = $this->_frontController->getRouter();
if (isset($_GET['lang'])) {
$lang = $_GET['lang'];
} else {
// auto recognition of language
$locale = new Zend_Locale();
$lang = $locale->getLanguage();
}
$langRoute = new Zend_Controller_Router_Route(
':lang/',
array(
'lang' => $lang
)
);
$defaultRoute = new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index'
)
);
$defaultRoute = $langRoute->chain($defaultRoute);
$adminRoute = new Zend_Controller_Router_Route(
'admin/:controller/:action/*',
array(
'module' => 'admin',
'controller' => 'index',
'action' => 'index'
)
);
$router->addRoute('langRoute', $langRoute);
$router->addRoute('defaultRoute', $defaultRoute);
$router->addRoute('adminRoute', $adminRoute);
}
You need to chain all the routes from the language route. See here:
http://robertbasic.com/blog/chaining-routes-in-zend-framework/
Two modules:
$defaultRoute = new Zend_Controller_Router_Route(
':module/:controller/:action',
This is a plugin that is universal and allow us to create your own routes without remembering about language. It's also setting Zend_Translator. This is a base class, to improve speed I'm suggesting to use Zend_Cache because code below impacts in code efficiency (I would say it's required if you have +100 routes).
<?php
class PsScripts_Controller_Plugin_Lang extends Zend_Controller_Plugin_Abstract {
private function initTranslator($locale){
$translate = new Zend_Translate(array('adapter' => 'tmx',
'content' => APPLICATION_PATH . '/configs/translations.tmx',
'locale' => $locale));
Zend_Registry::set('Zend_Translate', $translate);
}
public function routeStartup(\Zend_Controller_Request_Abstract $request) {
$locale = new Zend_Locale('pl_PL'); //default locale
if (preg_match('/\/([a-z]{2})([\/].*)/', $request->getRequestUri(),$matches)){ //if locale is found in request
$lang = $matches[1]; //obtain locale
/* #var $locale Zend_Locale */
switch ($lang){
case 'en':
$locale->setLocale('en_GB');
break;
}
Zend_Registry::set('Zend_Locale',$locale);
$this->initTranslator($locale);
$router = Zend_Controller_Front::getInstance()->getRouter();
/* #var $router Zend_Controller_Router_Rewrite */
$langRoute = new Zend_Controller_Router_Route(
':lang',
array(
'lang' => $lang
),
array(
'lang' => '[a-z]{0,2}'
)
);
$routes = $router->getRoutes();
foreach ($routes as $name=>$route){
if ($name != 'default'){
/* #var $route Zend_Controller_Router_Route */
$router->removeRoute($name);
/* #var $lang Zend_Controller_Router_Route */
$chain = new Zend_Controller_Router_Route_Chain();
$chain->chain($langRoute)->chain($route);
$router->addRoute($name,$chain);
}
}
$router->route($request);
} else {
$this->initTranslator($locale);
}
parent::routeStartup($request);
}
}

Categories