So i have been designig an application to run on the Zend Framework 1.11 And as any programmer would do when he sees repeated functionalities i wanted to go build a base class with said functionalities.
Now my plan is to build a library 'My' so i made a folder in the library directory in the application. So it looks like this
Project
Application
docs
library
My
public
test
So i created a BaseController class in the My folder and then decided to have the IndexController in my application extend the BaseController.
The Basecontroller looks like this :
class My_BaseController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->test = 'Hallo Wereld!';
}
}
And the IndexController looks like this :
class WMSController extends My_BaseController
{
public function indexAction()
{
parent::indexAction();
}
}
As adviced by a number of resources i tried adding the namespace for the library in the application.ini using the line
autoloadernamespaces.my = “My_”
But when i try to run this application i recieve the following error
Fatal error: Class 'My_BaseController' not found in
C:\wamp\www\ZendTest\application\controllers\IndexController.php
Am i missing something here? Or am i just being a muppet and should try a different approach?
Thanks in advance!
Your original approach will work for you in application.ini, you just had a couple of problems with your set up.
Your application.ini should have this line:-
autoloadernamespaces[] = "My_"
Also, you have to be careful with your class names, taking your base controller as an example, it should be in library/My/Controller/Base.php and should look like this:-
class My_Controller_Base extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->test = 'Hello World!';
}
}
You can then use it like this:-
class WMSController extends My_Controller_Base
{
public function indexAction()
{
parent::indexAction();
}
}
So, you had it almost right, but were missing just a couple of details. It is worth getting to know how autoloading works in Zend Framework and learning to use the class naming conventions
I don't know about .ini configuration, but I add customer libraries like this (index.php):
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->registerNamespace('My_');
Related
In my app, I was testing Google Directions API with ajax, but since I was just testing all the logic was in the routes.php file. Now I want to do things the proper way and have three layers: route, controller and service.
So in the routes I tell Laravel which method should be executed:
Route::get('/search', 'DirectionsAPIController#search');
And the method just returns what the service is supposed to return:
class DirectionsAPIController extends BaseController {
public function search() {
$directionsSearchService = new DirectionsSearchService();
return $directionsSearchService->search(Input::all());
}
}
I created the service in app/libraries/Services/Directions and called it DirectionsSearchService.php and copied all the logic I developed in routes:
class DirectionsSearchService {
public function search($input = array()) {
$origin = $input['origin'];
$destination = $input['destination'];
$mode = $input['mode'];
// do stuf...
return $data;
}
}
I read the docs and some place else (and this too) and did what I was supposed to do to register a service:
class DirectionsAPIController extends BaseController {
public function search() {
App::register('libraries\Services\Directions\DirectionsSearchService');
$directionsSearchService = new DirectionsSearchService();
return $directionsSearchService->search(Input::all());
}
}
// app/libraries/Services/Directions/DirectionsSearchService.php
use Illuminate\Support\ServiceProvider;
class DirectionsSearchService extends ServiceProvider {
}
I also tried adding libraries\Services\Directions\DirectionsSearchService to the providers array in app/config/app.php.
However, I am getting this error:
HP Fatal error: Class
'libraries\Services\Directions\DirectionsSearchService' not found in
/home/user/www/my-app-laravel/bootstrap/compiled.php on line 549
What am I doing wrong? And what is the usual way to use your own services? I don't want to place all the logic in the controller...
2 main things that you are missing:
There is a difference between a ServiceProvider and your class. A service provider in Laravel tells Laravel where to go look for the service, but it does not contain the service logic itself. So DirectionsSearchService should not be both, imho.
You need to register your classes with composer.json so that autoloader knows that your class exists.
To keep it simple I'll go with Laravel IoC's automatic resolution and not using a service provider for now.
app/libraries/Services/Directions/DirectionsSearchService.php:
namespace Services\Directions;
class DirectionsSearchService
{
public function search($input = array())
{
// Your search logic
}
}
You might notice that DirectionsSearchService does not extend anything. Your service becomes very loosely coupled.
And in your DirectionsAPIController.php you do:
class DirectionsAPIController extends BaseController
{
protected $directionsSearchService;
public function __construct(Services\Directions\DirectionsSearchService $directionsSearchService)
{
$this->directionsSearchService = $directionsSearchService;
}
public function search()
{
return $this->directionsSearchService->search(Input::all());
}
}
With the code above, when Laravel tries to __construct() your controller, it will look for Services\Directions\DirectionsSearchService and injects into the controller for you automatically. In the constructor, we simply need to set it to an instance variable so your search() can use it when needed.
The second thing that you are missing is to register your classes with composer's autoload. Do this by adding to composer.json's autoload section:
"autoload": {
"classmap": [
... // Laravel's default classmap autoloads
],
"psr-4": {
"Services\\": "app/libraries/Services"
}
}
And do a composer dump-autoload after making changes to composer.json. And your code should be working again.
The suggestion above can also be better with a service provider and coding to the interface. It would make it easier to control what to inject into your controller, and hence easier to create and inject in a mock for testing.
It involves quite a few more steps so I won't mention that here, but you can read more in Exploring Laravel’s IoC container and Laravel 4 Controller Testing.
This is my trouble:
Fatal error: Class 'Default_Model_Category' not found in
/home/u988704384/public_html/mutilmodules1/application/modules/default/controllers/IndexController.php
on line 89
But in localhost, it run normal.
Please help me solve it.
In file category.php:(/public_html/mutilmodules1/application/modules/default/models/)
<?php
class Default_Model_Category extends Zend_Db_Table
{
protected $_name = 'categories';
public function listinfo()
{
$data=$this->fetchall();
return $data;
}
}
In file IndexController.php: (/public_html/mutilmodules1/application/modules/default/controllers/)
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$category = new Default_Model_Category();
$this->view->entries = $category->listinfo();
}
}
The Zend Framework classes are either missing from your library folder, or not readable by the web server. First step is to see whether /home/u988704384/public_html/mutilmodules1/application/library/Zend/Application.php exists. If so, check the permissions on the library folder.
Could your current issue be as simple as a case issue? Your class is named Default_Model_Category but you seem to indicate that the file name is category.php. Try renaming the file to Category.php and see if that corrects the issue. I know I've run into this issue before when developing on my Mac, which doesn't care about case, but my LAMP server does.
I have the problem that in my ZF App the action helper cannot be loaded. The error message is:
Action Helper by name Sunshine not found
The layout of my ZF app uses modules where I have the following structure:
application
modules
weather
controllers
helpers
I have registered the helper in the modules Bootstrap which is located in
application -> modules -> weather -> Bootstrap.php
Here is the code
<?php
class Weather_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initActionHelperBrokers()
{
Zend_Controller_Action_HelperBroker::addPath('controllers/helpers', 'Weather_Controllers_Action_Helper_');
}
}
<?php
class Weather_Controller_Action_Helper_Sunshine extends Zend_Controller_Action_Helper_Abstract
{
public function getSunrise()
{
return "06:00";
}
}
<?php
class Weather_ForecastsController extends Zend_Controller_Action
{
protected function getForecasts($date)
{
$sunrise = $this->_helper->Sunshine->getSunrise();
// tbc
}
What is it, what I'm doing wrong here?
EDIT: As suggested I tried to add the helper in the bootstrap with the full path, but I got the same error.
the addPath method expects the entire path to the helper directory. Change it to something like:
Zend_Controller_Action_HelperBroker::addPath(APPLICATION_PATH . '/modules/weather/controllers/helpers', 'Weather_Controllers_Action_Helper_');
Alternatively, you can also add the paths via your application.ini:
resources.frontController.actionhelperpaths.Weather_Controllers_Action_Helper = APPLICATION_PATH "/modules/weather/controllers/helpers"
Where do i place the user defined functions in zend framework. These functions will used across the framework in many controls, views or models. Do i need to convert this to a utility class? Or i can just keep it as a set of functions and include it in index.php.
what is the best practice for this?
Typically you would put your functions into a class in the library for the auto loader. Use the naming conventions for ZF to make life easier.
adjust your application.ini to add a namespace.
Examples:
//application.ini
autoloaderNamespaces[] = "My_"
//this would equate to the folder My in the folder library
/application
/library
/My
//any class you built would be named My_Classname and be called in your app by Classname()
<?php
class My_Classname {
public function myFunction() {
}
}
//in your conrtoller for example you might call
public function indexAction() {
$class = new My_Classname();
$class->myFunction();
//or if you declared myFunction() static...
$class = My_Classname::myFunction();
}
Make it by following ZF directory structure:
Make Action Helpers for Controllers and View Helpers for Views :
In your library folder which is set in set_include_path:
create library/My/View/Helper/Common.php
Like below:
class My_View_Helper_Common extends Zend_View_Helper_Abstract
{
public function common()
{
return $this;
}
public function getCity($id)
{
$registry = Zend_Registry::getInstance();
$DB = $registry['DB'];
$result = $DB->fetchPairs("select * from firm_dtl");
return $result;
}
}
OR Call in View:
$this->common()->getCity($id);
Same process fro action helpers:
Make in library/My/Action/Helper/Common.php
This is my directory Structure
application
---modules
------admin
---------models
-----------User.php
This is my user Model class
class admin_Model_User
{
//User.php
}
This is my UserTest Class with simple AssertType
class admin_Model_UserTest
extends PHPUnit_Framework_TestCase
{
public function testUserModel()
{
$testUser = new admin_Model_User();
$this->assertType("admin_Model_User",$testUser);
}
}
When I run this. I am getting following Errors
[tests]# phpunit
PHPUnit 3.5.13 by Sebastian Bergmann.
0
Fatal error: Class 'admin_Model_User' not found in /web/zendbase/tests/application/modules/admin/models/UserTest.php on line 18
I know there my must be some path setting. I really could not able to figure out what is really wrong. Looking for help.....
You need to bootstrap Zend in your project's PHPUnit bootstrap.php file. Even though you are testing models and thus don't need the dispatcher, you must still have Zend_Application load application.ini and register its autoloader.
You can use Zend_Test_PHPUnit_ControllerTestCase to do the bootstrapping instead and make sure your model tests run after one of these, but that's a bit hacky.
Another option is to require_once the model classes manually for each test. The reason this doesn't work automatically via PHPUnit's autoloader is that it doesn't know how to transform the "namespace" admin_Model into the path admin/models.
Finally, you could write a simple autoloader to replace the one in PHPUnit. Before converting underscores to slashes, check if the class begins with the "namespace" above and replace it if so.
All I need to do is this
//file:ControllerTestCase.php
<?php
require_once GLOBAL_LIBRARY_PATH. '/Zend/Application.php';
require_once GLOBAL_LIBRARY_PATH. '/Zend/Test/PHPUnit/ControllerTestCase.php';
abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
protected $_application;
protected function setUp()
{
$this->bootstrap = array($this, 'appBootstrap');
parent::setUp();
}
public function appBootstrap()
{
$this->_application = new Zend_Application(APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$this->_application->bootstrap();
/**
* Fix for ZF-8193
* http://framework.zend.com/issues/browse/ZF-8193
* Zend_Controller_Action->getInvokeArg('bootstrap') doesn't work
* under the unit testing environment.
*/
$front = Zend_Controller_Front::getInstance();
if($front->getParam('bootstrap') === null) {
$front->setParam('bootstrap', $this->_application->getBootstrap());
}
}
}
// and then require_once it in Bootstrap file.
thats all :) it is working.