Issue with Zend framework controller - php

I am a newbie to the Zend framework.
I am getting an error while loading my index controller:
Fatal error: Class 'Places' not found in C:\xampp\htdocs\zend\book\application\controllers\IndexController.php on line 36
My bootstrapper code is
<?php
class Bootstrap
{
public function __construct($configSection)
{
$rootDir = dirname(dirname(__FILE__));
define('ROOT_DIR', $rootDir);
set_include_path(get_include_path(). PATH_SEPARATOR . ROOT_DIR . '/library/'. PATH_SEPARATOR . ROOT_DIR .
'/application/models/');
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
// Load configuration
Zend_Registry::set('configSection',$configSection);
$config = new Zend_Config_Ini(ROOT_DIR.'/application/config.ini',$configSection);
Zend_Registry::set('config', $config);
date_default_timezone_set($config->date_default_timezone);
// configure database and store to the registry
$db = Zend_Db::factory($config->db);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
Zend_Registry::set('db', $db);
}
public function configureFrontController()
{
$frontController = Zend_Controller_Front::getInstance();
$frontController->setControllerDirectory(ROOT_DIR .'/application/controllers');
}
public function runApp()
{
$this->configureFrontController();
// run!
$frontController = Zend_Controller_Front::getInstance();
$frontController->dispatch();
}
}
I have a model:
<?php
class Places extends Zend_Db_Table
{
protected $_name = 'places'; //table name
function fetchLatest($count = 10)
{
return $this->fetchAll(null,'date_created DESC', $count);
}
}
My index controller code is:
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Welcome';
$placesFinder = new Places();
$this->view->places = $places->fetchLatest();
}
}
I am using ZF version 1.10.4

There is a good chance you are missing somethign in your class declaration
try:
<?php
class Models_Places extends Zend_Db_Table
{
protected $_name = 'places'; //table name
function fetchLatest($count = 10)
{
return $this->fetchAll(null,'date_created DESC', $count);
}
}
The Zend autoloader class will look into Models/places.php for your class.
Also you could initialise the models and default module in bootstrap with:
protected function _initAutoload() {
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => '',
'basePath' => dirname(__FILE__),
));
$autoloader->addResourceType('models', 'models/', 'Models');
return $autoloader;
}
After having done that your class should be named Models_Places.
Check out the docs about autoloading.

Well, personally, I use extended controllers which contain few util methods I use very often. Here is a snippet of my extended controller:
<?php
class My_MyController extends Zend_Controller_Action
{
protected $_tables = array();
protected function _getTable($table)
{
if (false === array_key_exists($table, $this->_tables)) {
include APPLICATION_PATH . '/modules/'
. $this->_request->getModuleName() . '/models/' . $table . '.php';
$this->_tables[$table] = new $table();
}
return $this->_tables[$table];
}
}
You just need to define the APPLICATION_PATH in index.php. Then your controller could look like this:
<?php
class IndexController extends My_MyController
{
public function indexAction()
{
// get model
$model = $this->_getTable('ModelName');
}
}
Path where you store the My_Controller must also be in your include path.

Related

How to use namespace properly in PHP?

I have this file root/core/Router.php
<?php
namespace Core;
class Router {
protected $url;
protected $controller;
private function parseURL() {
// threat the $this->url; for example ["r", "product"]
}
private function request() {
$this->controller = Controller::get($this->url[1]);
}
public function __construct() {
$this->parseURL();
$this->request();
}
}
?>
then file root/core/Controller.php
<?php
namespace Core;
class Controller {
public static function model($name, $params = []) {
$model = "\\Model\\$name";
return new $model($params);
}
public static function view($name, $params = []) {
require_once APP_DIR . "view/" . $name . ".php";
}
public static function get($name, $params = []) {
require_once APP_DIR . "controller/" . $name . ".php";
$name = "\\Controller\\$name";
return new $name($params);
}
}
?>
then root/controler/Product.php
<?php
namespace Controller;
use Core\Controller;
use Model\Product;
class Product {
public function get() {
$ret['state'] = 510;
$productModel = new Product;
$products = $productModel->getAll();
if(isset($products)) {
$ret['products'] = $products;
$ret['state'] = 200;
}
return $ret;
}
}
?>
then file root/model/Product.php
<?php
namespace Model;
class Product {
public function add($values) {
return Database::insert("product", $values);
}
}
?>
and root/core/Model.php
<?php
namespace Core;
class Model {
protected $table = null;
public function getAll() {
// some code to collect data
}
}
?>
What i want to achive is that every Controller in root/controller/*.php able to load any Model in root/model/*.php but class inside root/model/*.php must able to access (inheritance/extends) the Model class inside root/core/Model.php i firstly asked on chatGPT for some AI Generated answer, that the reason why i get this far.
Then i get this error, when the AI keep giving the same answer.
Fatal error: Cannot declare class Controller\Product because the name is already in use in C:\xampp\htdocs\app\shop\controller\Product.php on line 6
I actually realize that the simple way probably with naming the class so ther no conflict between it but i became aware how to properly using the namespace if its such features in php. Those files loaded without any autoloader, so i just require_once each file in root/init.php file.
I read few documentations but hard to implement in multiple files and directorys.
I Apreciate any feedback, thanks

getServiceLocator returning Null under PHPUnittests

I'm trying to test a simple controller that authenticates a user using the LdapAdapter and using the 'ldap' array from the configuration of the Application, but phpunit is returning the following error:
Fatal error: Uncaught Error: Call to a member function get() on null in /var/www/html/app/module/Auth/src/Auth/Controller/AuthController.php:53
Stack trace:
#0 /var/www/html/app/module/Auth/test/AuthTest/Controller/AuthControllerTest.php(37): Auth\Controller\AuthController->authenticate('myuser', 'mypassword')
#1 [internal function]: AuthTest\Controller\AlbumControllerTest->testLoginAction()
#2 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(863): ReflectionMethod->invokeArgs(Object(AuthTest\Controller\AlbumControllerTest), Array)
#3 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(741): PHPUnit_Framework_TestCase->runTest()
#4 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php(608): PHPUnit_Framework_TestCase->runBare()
#5 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(697): PHPUnit_Framework_TestResult->run(Object(AuthTest\Controller\AlbumControllerTest))
#6 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestSuite.php(733): PHPUnit_Framework_TestCase- in /var/www/html/app/module/Auth/src/Auth/Controller/AuthController.php on line 53
My Controller is the following:
<?php
namespace Auth\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Authentication\Adapter\Ldap as AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Result;
use Auth\Form\AuthForm;
use Auth\Model\Auth;
class AuthController extends AbstractActionController
{
public function loginAction()
{
$form = new AuthForm();
$request = $this->getRequest();
if ($request->isPost()) {
$auth = new Auth();
$form->setInputFilter($auth->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()){
$auth->exchangeArray($form->getData());
$values = $form->getData();
$result = $this->authenticate($values['username'], $values['password']);
switch($result->getCode()) {
case Result::SUCCESS:
return $this->redirect()->toRoute('home');
break;
case Result::FAILURE:
break;
}
}
}
return array('form' => $form);
}
public function authenticate($username, $password){
$options = $this->getServiceLocator()->get('Config');
$authAdapter = new AuthAdapter($options['ldap'],
'username',
'password');
$authAdapter
->setIdentity($username)
->setCredential($password);
$auth = new AuthenticationService();
$result = $auth->authenticate($authAdapter);
return $result;
}
private function debug($var){
echo '<pre>';
var_dump($var);
echo '</pre>';
exit();
}
}
The TestCase:
namespace AuthTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Authentication\AuthenticationService;
use Auth\Controller\AuthController;
class AuthControllerTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include '/var/www/html/app/config/application.config.php'
);
parent::setUp();
}
public function testLoginAction()
{
#Basic Access to the page
$this->dispatch('/login');
$this->assertResponseStatusCode(200);
$data = array(
'identity' => 'myuser',
'credential' => 'mypassword',
);
$auth = new AuthController();
$auth->authenticate($data['identity'], $data['credential']);
$identity = new AuthenticationService();
$this->assertEquals($data['identity'], $identity->getIdentity());
}
}
PHPUnittest's BootStrap:
<?php
namespace AuthTest;
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
/**
* Test bootstrap, for setting up autoloading
*/
class Bootstrap
{
protected static $serviceManager;
public static function init()
{
$zf2ModulePaths = array(dirname(dirname(__DIR__)));
if (($path = static::findParentPath('vendor'))) {
$zf2ModulePaths[] = $path;
}
if (($path = static::findParentPath('module')) !== $zf2ModulePaths[0]) {
$zf2ModulePaths[] = $path;
}
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$config = array(
'module_listener_options' => array(
'module_paths' => $zf2ModulePaths,
),
'modules' => array(
'Auth'
)
);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
}
public static function chroot()
{
$rootPath = dirname(static::findParentPath('module'));
chdir($rootPath);
}
public static function getServiceManager()
{
return static::$serviceManager;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (file_exists($vendorPath.'/autoload.php')) {
include $vendorPath.'/autoload.php';
}
if (! class_exists('Zend\Loader\AutoloaderFactory')) {
throw new RuntimeException(
'Unable to load ZF2. Run `php composer.phar install`'
);
}
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();
Bootstrap::chroot();
In the functional tests it works as expected, but on php unittests the error occurs in the line 53 '$options = $this->getServiceLocator()->get('Config');'.
So, how can use or set ServiceLocator to work with phpunittests?
After so much struggling on this doubt and other tests which a controller that uses an ServiceLocator, I figure that the correct way to test a controllers Action is to Mock the object or/and his methods.
I don't feel totally comfortable with this solution, but for now, this is what it is.
An other feel is that mocking an behaviour of the code is something like that breaks the DRY principle: instead of use the real code I just create a mock that half behaves like it :S
Anyway, the following code does the job for me:
$authControllerMock = $this->getMockBuilder('Auth\Controller\AuthController')
->disableOriginalConstructor()
->getMock();
$authControllerMock->expects($this->any())
->method('authenticate')
->will($this->returnValue(true);
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService('Auth\Controller\Auth', $authControllerMock);
$this->dispatch('/usermgr');
self::assertMatchedRouteName('usermgr');
self::assertControllerClass('AuthController');
self::assertControllerName('auth\controller\auth');
self::assertResponseStatusCode('200');

PHP require files when use namespace

I'm new to PHP namespace. and there is a problem when I use auto-load.
ROOT/Application/Instance.php
<?php
namespace Application;
class Instance {
public static $_database;
public function __construct() {
self::$_database = new \Application\Module\Database();
}
public static function database() {
return self::$_database;
}
public static function ID(){
return md5(uniqid(mt_rand(), TRUE) . mt_rand() . uniqid(mt_rand(), TRUE));
}
public static function autoload($_className) {
$thisClass = str_replace(__NAMESPACE__.'\\', '', __CLASS__);
$baseDir = __DIR__;
if (substr($baseDir, -strlen($thisClass)) === $thisClass) {
$baseDir = substr($baseDir, 0, -strlen($thisClass));
}
$_className = ltrim($_className, '\\');
$fileName = $baseDir;
$namespace = '';
if ($lastNsPos = strripos($_className, '\\')) {
$namespace = substr($_className, 0, $lastNsPos);
$_className = substr($_className, $lastNsPos + 1);
$fileName .= str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $_className) . '.php';
if (file_exists($fileName)) {
require $fileName;
}
}
public static function registerAutoloader() {
spl_autoload_register(__NAMESPACE__ . "\\Instance::autoload");
}
}
ROOT/Application/Module/Database.php
<?php
namespace Application\Module;
include 'FluentPDO/FluentPDO.php';
class Database extends Module {
public static $_instance;
public function __construct() {
if(self::$_instance === NULL) {
self::$_instance = new FluentPDO(new PDO("mysql:host=8273639.mysql.rds.aliyuncs.com;dbname=db", 'name', 'password'));
}
}
}
When I run this:
new \Application\Instance();
I got this error:
Fatal error: Class 'Application\Module\FluentPDO' not found in /mnt/www/airteams_com/public/Application/Module/Database.php on line 13
I'm pretty sure that 'FluentPDO/FluentPDO.php' exists. and the error shows a wrong path of the file. the right path is 'ROOT/Application/Module/FluentPDO/FluentPDO.php'
So how can i use a no namespace class in my situation? thanks.
When you are working with namespaces you must fully qualify each class unless it's a child of the current namespace.
As such the FluentPDO is probably on the root namespace which means you need to access it like such:
self::$_instance = new \FluentPDO(new \PDO("mysql:host=8273639.mysql.rds.aliyuncs.com;dbname=db", 'name', 'password'));

Factory Class not found while testing ZF2 app

I'm new on ZF2 and I have a problem to test a Factory Class.
This is my contollers.config.php
<?php
return array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController',
'Application\Controller\SystemStatus' => 'Application\Controller\SystemStatusController',
),
'factories' => array(
'Application\Controller\HomePage' => 'Application\Controller\Factory\HomePageControllerFactory',
),
);
My path to the Factory Class is:
/app/src/module/Application/src/Application/Controller/Factory/HomePageControllerFactory.php
And the file looks like:
<?php
namespace Application\Controller\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Controller\HomePageController;
class HomePageControllerFactory implements FactoryInterface {
/**
* #param ServiceLocatorInterface $sm
* #return HomePageController|mixed
*/
public function createService(ServiceLocatorInterface $sm)
{
$queryParams = $sm->getServiceLocator()->get('path\path');
$service = $sm->getServiceLocator()->get('path\path');
return new HomePageController($queryParams, $service);
}
}
My path to the Test is:
/app/tests/ApplicationTest/Integration/Controller/Factory/HomePageControllerFactoryTest.php
and the only that I have inside is:
<?php
namespace ApplicationTest\Integration\Controller\Factory;
use Application\Controller\Factory\HomePageControllerFactory;
use PHPUnit_Framework_TestCase as TestCase;
class HomePageControllerFactoryTest extends TestCase
{
/**
* #var HomePageControllerFactory
*/
private $underTest;
public function setUp()
{
$this->underTest = new HomePageControllerFactory();
}
public function testCreateService()
{
/*Nothing*/
}
}
and when I run the test this is the ouput
PHP Fatal error: Class 'Application\Controller\Factory\HomePageControllerFactory' not found in /apps/tests/ApplicationTest/Integration/Controller/Factory/HomePageControllerFactoryTest.php on line 17
Bootstrap.php is located /app/tests/Bootstrap.php
content:
<?php
namespace ApplicationTest;
use Zend\Loader\AutoloaderFactory
, Zend\Mvc\Service\ServiceManagerConfig
, Zend\ServiceManager\ServiceManager;
date_default_timezone_set('America/New_York');
error_reporting(E_ALL | E_STRICT);
chdir(dirname(__DIR__));
require 'vendor/autoload.php';
class Bootstrap
{
private static $instance;
private $serviceManager;
public static function getInstance()
{
if (!isset(static::$instance)) {
$class = get_class();
static::$instance = new $class;
}
return static::$instance;
}
public static function init()
{
AutoloaderFactory::factory();
static::getInstance();
}
public function getServiceManager()
{
if (!isset($this->serviceManager)) {
$this->serviceManager = new ServiceManager(new ServiceManagerConfig());
}
return $this->serviceManager;
}
public function setServiceManager(ServiceManager $sm)
{
$this->serviceManager = $sm;
}
public function getConfig()
{
if (is_readable(__DIR__ . '/TestConfig.php')) {
return require __DIR__ . '/TestConfig.php';
} else {
return require './src/config/application.config.php';
}
}
private function __construct()
{
}
}
Bootstrap::init();
I'm stuck on this, I don't know why I can not instance an object of HomePageControllerFactory
If you're using composer then you will need to include the path to the Application classes in the autoload section of composer.json:
...
"autoload": {
"psr-0": {
"Application": "src/module/Application/src/"
}
}
...
And then rerun composer to generate the autoload files again:
$ php composer.phar dump-autoload

Zend framework with default and mobile layout

currently i have a zend framework application, i integrate it with wurfl user agent, so i can switch between mobile and desktop version, my plugin reside in library
<?php
class Zc_Controller_Plugin_TemplatePicker extends Zend_Controller_Plugin_Abstract
{
protected $useragent;
public function postDispatch(Zend_Controller_Request_Abstract $request)
{
$bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
$this->useragent = $bootstrap->getResource('useragent');
if($this->useragent->getDevice()->getType() == 'mobile')
{
Zend_Layout::getMvcInstance()->setLayout('mobile');
}
}
}
and now i have 2 layouts in script file mobile.phtml and layout.phtml, hw can i use some of the controller function so that it can be use for mobile layout, also i have layout loader in bootstrap
protected function _initLayoutHelper()
{
// $front = Zend_Controller_Front::getInstance();
// $front->registerPlugin(new Zc_Controller_Plugin_TemplatePicker());
if(!stristr($_SERVER['REQUEST_URI'], '/admin')){
$this->bootstrap('frontController');
}
$layout = Zend_Controller_Action_HelperBroker::addHelper(new Zc_Controller_Action_Helper_LayoutLoader());
}
and the layoutloader code is
<?php
class Zc_Controller_Action_Helper_LayoutLoader extends
Zend_Controller_Action_Helper_Abstract
{
public function preDispatch(){
$bootstrap = $this->getActionController()->getInvokeArg('bootstrap');
$config = $bootstrap->getOptions();
Zend_Registry::set('config', $config);
$module = $this->getRequest()->getModuleName();
$controller = $this->getRequest()->getControllerName();
$action = $this->getRequest()->getActionName();
$layoutScript = "layout";
if (isset($config[$module]['resources']['layout']['layout'])) {
$layoutScript = $config[$module]['resources']['layout']['layout'];
}
$this->getActionController()->getHelper('layout')->setLayout($layoutScript);
}
}
where should i tweak now so that i can have 1 controller class with 2 separate layout.Thanks!!
If you wish to set some certain layout for the certain controller, you can use the following code:
class Custom_Plugin_Layout extends Zend_Controller_Plugin_Abstract
{
public function preDispatch()
{
$front = Zend_Controller_Front::getInstance();
$layout = Zend_Layout::getMvcInstance();
switch ($front->getRequest()->getControllerName()) {
case "index":
$layout->setLayout('home');
break;
case "login":
$layout->setLayout('login');
break;
default:
$layout->setLayout('default');
}
}
}

Categories