Lithium PHP integration testing - routes.php not included? - php

I'm building a toy app in Lithium (PHP framework) based upon the Union of RAD's Framework project. It's all working great in the browser but when running integration tests, routes.php is not loaded, so the routing isn't working.
Here's the code I'm testing:
class StaffController extends \lithium\action\Controller {
public function add() {
$staff = Staff::create();
if (($this->request->data) && $staff->save($this->request->data)) {
return $this->redirect(array('Staff::view', 'args' => array($staff->id)));
}
return compact('staff');
}
My test:
public function testAdd() {
//Router::connect('/{:controller}/{:action}/{:args}');
$request = new Request();
$request->data = array('name' => 'Brand new user');
$controller = new StaffController(array('request' => $request));
/* #var $response \lithium\action\Response */
$response = $controller->add();
$this->assertEqual(302, $response->status['code']);
}
Notice the commented out line - Router::connect('/{:controller}/{:action}/{:args}'); - if I uncomment that, it's all good.
What I'm puzzled about is why, when running in unit tests, app/config/routes.php (where I define my routes) isn't loaded. From what I can determine, app/config/bootstrap/action.php adds a filter to the "run" method of the Dispatcher which loads routes.php.
Of course, it's possible that I am totally missing the point here! I'd appreciate any guidance you can give me!

Lithium has a lithium\action\Dispatcher used for http requests and a lithium\console\Dispatcher for console commands.
I'm assuming you are running tests from the command-line. I'm looking at the "framework" project's app/config/bootstrap/action.php file (here on github).
It is only including the routes.php file for the lithium\action\Dispatcher which is not loaded from the command-line. The app/config/bootstrap/console.php also doesn't include routes.php for the console.
My suggestion is to edit the console.php file and change the filter to look like this:
Dispatcher::applyFilter('run', function($self, $params, $chain) {
Environment::set($params['request']);
foreach (array_reverse(Libraries::get()) as $name => $config) {
if ($name === 'lithium') {
continue;
}
$file = "{$config['path']}/config/routes.php";
file_exists($file) ? call_user_func(function() use ($file) { include $file; }) : null;
}
return $chain->next($self, $params, $chain);
});

Related

Phalcon PHP framework not passing parameters to controller

Just started development in the Phalcon PHP framework and am also pretty new to PHP in general. My question is on how to create a request with a route, which I believe I have done, and pass the parameters of the route to the controller action that is linked to the said route. Below I have included the three files that I have been working on and summarize what each one is supposed to do. I also have the end result and where my problem lies directly.
The first file is the index.php file that takes in all route requests for my site.
<?php
//Include all routes on site
foreach (glob("../app/routes/*.php") as $filename)
{
include $filename;
}
foreach (glob("../app/controllers/*.php") as $filename)
{
include $filename;
}
//Create routes and initialize routes
$router = new \Phalcon\Mvc\Router();
$router->mount(new PublicRoutes());
$router->mount(new ApiRoutes());
$router->mount(new AdminRoutes());
$router->handle();
$controller = $router->getControllerName();
$action = $router->getActionName();
$params = $router->getParams();
$di = new \Phalcon\DI\FactoryDefault();
$d = new Phalcon\Mvc\Dispatcher();
$d->setDI($di);
$d->setControllerName($router->getControllerName());
$d->setActionName($router->getActionName());
$d->setParams($router->getParams());
$controller = $d->dispatch();
The second file is the actual routes mounted in for my API call which I am testing everything out with.
<?php
class ApiRoutes extends Phalcon\Mvc\Router\Group
{
public function initialize()
{
//Basic api route for pixelpusher
$this->add(
"/addhawk/api/:action/:model/:params",
array(
"controller" => "api",
"action" => 1,
"model" => 2,
"params" => 3,
)
);
}
}
The third, and final file is the controller class for the API with the only action I am testing right now.
<?php
class ApiController extends \Phalcon\Mvc\Controller
{
public function handlerAction()
{
//Pull in parameters
echo "<h1>API Handler Entered</h1>";
$model = $this->dispatcher->getParam("model");
echo $model;
//Choose correct api based off of api param
if( $model == "grid" ) {
echo 'grid';
}
else if ( $model == "admin" ) {
echo 'admin';
}
else {
//No valid api must have been found for request
}
//Return result from api call
return true;
}
}
So, the url is "localhost/addhawk/api/handler/grate/view" which results in the following output in html courtesy of line 9 in the ApiController.
There is no print out of the $model variable as it should do. There is also no error so I have no idea why it's not printing. According to the documentation and every resource I have read online, all parameters should be available directly from each controller action thanks to the dispatcher and $di class or something similar. So my question is why can I not access the parameters if everything seems to be saying I should be able to?

yii - how to create a completely independant extension using nusoap 0.9.5 in yii 1.1.13

SOLVED Thanks to #JPR and tuned-up thanks to #PeterM
/* The only dependance is when */ class NuSoap extends CApplicationComponent
v-Below, the initial question -v
I would like to know how to create a basic extenstion in yii 1.1.13 using nusoap 0.9.5?
My simple code looks like this :
<?php
require("libs/nusoap-0.9.5/lib/nusoap.php");
// namespace
$ns = "https://my-namespace-site";
// client
$client = new soapclient('https://ip-to-webservice-server');
// header
$headers = "<credentials><ns1:username xmlns:ns1=\"$ns\">username</ns1:username>
<ns2:password xmlns:ns2=\"$ns\">password</ns2:password></credentials>";
$client->setHeaders($headers);
// searching
$params = array(
'local_user_array' => array(
'limit' => 10
)
);
$result = $client->call('local_users_search', $params, $ns );
if( $client->getError() ) {
echo $client->getError();
}
else {
foreach( $result['data'] as $offer ) {
echo "<div>".$offer['firstname']."</div>";
}
}
?>
My code works perfectly. Now, what coud I do to use $result in yii to be able to show results in a view?
The best answer will be a concrete example with file structure and code plus meaningful explanations.
Any help would be greatly appreciated. Thanks for your help in advance. I'm looking forward to it ;-)
PS: please do not reference any links to yiiframework site because it doesn't help much as I also know how to search.
Make a class that extends from CApplicationComponent.
class NuSoap extends CApplicationComponent
{
protected $params = array();
protected $client, $ns;
public function init() {
require("libs/nusoap-0.9.5/lib/nusoap.php");
$this->client = new soapclient('https://ip-to-webservice-server');
$this->ns = "https://my-namespace-site";
}
public function getResults() {
$results = $this->client->call(
'local_users_search',
$this->params,
$this->ns
);
return $results;
}
public function setParams(array $params) {
$this->params = $params;
}
// whatever other methods you need for it to work
}
Then in your main config file, under the components array:
array(
'nuSoap' => array(
'class' => 'application.components.NuSoap' // name your class NuSoap.php
)
......
)
Make sure the application/components or application/extensions directory is imported in the main.php config file as well. Put your class file in NuSoap.php in the application/components or applcation/extensions directory.
You can now refer to your component anywhere in your Yii app:
Yii::app()->nuSoap->setParams($params);
$results = Yii::app()->nuSoap->getResults();
This should be plenty to get you started in the right direction. The Yii documentation would be very helpful in understanding how application components work, but since you don't want to read it you'll just have to guess on some things. If you want to use Yii it makes absolutely no sense to avoid reading the documentation.

Phalcon backup view path

Is there any way to pass through a secondary path to the views dir in phalcon?
in zend framework I think the syntax is
$this->view->addScriptPath('/backup/path');
$this->view->addScriptPath('/preferred/path');
so if there is a file in the preferred path it will use it, if not it will fallback through the chain.
I use this, for example, for mobile versions when most of the pages are the same, but some have to be significantly different and I don't want to have to duplicate all the views just for 2 or 3 variants
In phalcon I have tried sending an array to the view, but that just results in neither working
$di->set('view', function() use ($config) {
$view = new \Phalcon\Mvc\View();
$view->setViewsDir( array('/preferred/path/', '/backup/path/') );
return $view;
});
I've got this working by extending the Phalcon\Mvc\View\Engine\Volt
In the render($template_path, $params, $must_clean = null) method I set the alternative path, check if file is available and if so I switch the $template_path given with the alternative path. Then it's just a case of calling:
return parent::render($template_path, $params, $must_clean);
where $template_path contains the new (alternative) path.
If your alternative path might change on a per project basis and you need to set it in bootstrap, then rather than doing it when getting a "view" from di you would do it when getting volt.
Just remember that all views are rendered with that method so you will have to account for layout and partial views as well - depending on your implementation.
Example: (this has not been tested, it's based on a similar set up I have in my own code)
<?php
class Volt extends Phalcon\Mvc\View\Engine\Volt
{
private $skin_path;
public function render($template_path, $params, $must_clean = null)
{
$skin_template = str_replace(
$this->di->getView()->getViewsDir(),
$this->getSkinPath(),
$template_path
);
if (is_readable($skin_template)) {
$template_path = $skin_template;
}
return parent::render($template_path, $params, $must_clean);
}
public function setSkinPath($data)
{
$this->skin_path = $data;
}
public function getSkinPath()
{
return $this->skin_path;
}
}
In your bootstrap:
$di->setShared('volt', function($view, $di) {
$volt = new Volt($view, $di);
$volt->setSkinPath('my/alternative/dir/');
return $volt;
});
Many thanks to nickolasgregory#github who pointed me in the right direction.
Method proposed by #strayobject helps me also, but I've found that using extend or other statements inside volt templates dosn't work.
Here's refined solution that works with extend and include:
use Phalcon\Mvc\View\Engine\Volt;
class VoltExtension extends Volt
{
// Override default Volt getCompiler method
public function getCompiler()
{
if (!$this->_compiler) {
$this->_compiler = new VoltCompilerExtension($this->getView());
$this->_compiler->setOptions($this->getOptions());
$this->_compiler->setDI($this->getDI());
}
return $this->_compiler;
}
}
And
use Phalcon\Mvc\View\Engine\Volt;
class VoltCompilerExtension extends Volt\Compiler
{
public function compileFile($path, $compiledPath, $extendsMode = null)
{
$skinPath = $this->getOption('skinPath');
if ($skinPath) {
$skinTemplate = str_replace(
$this->getDI()->getView()->getViewsDir(),
$skinPath,
$path
);
if (is_readable($skinTemplate)) {
$path = $skinTemplate;
}
}
return parent::compileFile($path, $compiledPath, $extendsMode);
}
}
Usage:
$volt = new VoltExtension($view, $di);
$volt->setOptions(
array(
'compiledPath' => $config->application->cacheDir,
'compiledSeparator' => '_',
'compileAlways' => false,
'skinPath' => $config->application->skinPath
)
);
Please take a look at this phalcon framework update. It provides support for multiple view packages per website (you can have multiple websites). Users of the magento framework will find it easy to use:
https://github.com/alanbarber111/cloud-phalcon-skeleton

how to auto load mobile templates by agent in codeigniter?

dir:
application
-controllers
-models
-views
-mobile_views
How do I auto load templates at mobile_views when I use $this->load->view and view by iphone or other mobile phone?
Check this
You can do it in two way.
Way 1: Its very simple. In the above answer (the link I have given) add following line in the end of MyController function
$this->load->_ci_view_path . = $this->view_type .'/';
You are done. You can simply load view like normal view load.
Way 2:
To autoload a view based on user agent, I think you can implement it using hooks. To implement this hooks you need to follow the following steps
Autoload user agent library in autoload.php
$autoload['libraries'] = array('user_agent');
Enable hooks in config.php
$config['enable_hooks'] = TRUE;
Not implement hooks on post_controller_constructor. Add following codes to hooks.php
$hook['post_controller_constructor'][] = array('class' => 'Loadview',
'function' => 'load',
'filename' => 'loadview.php',
'filepath' => 'hooks'
);
Now create a page named loadview.php under hooks directory having following code
class Loadview
{
public static $MOBILE_PLATFORM = 'mobile';
public static $DEFAULT_PLATFORM = 'default';
public function load(){
$this->CI =& get_instance();
$view_type = $this->CI->agent->is_mobile() ? self::$MOBILE_PLATFORM : self::$DEFAULT_PLATFORM;
$this->CI->load->_ci_view_path = $this->CI->load->_ci_view_path . $view_type .'/';
}
}
You are done now. You can simply load view like normal view load.
to load views from another dir aside from "views", i found this forum topic to be helpful
http://codeigniter.com/forums/viewthread/132960/
function external_view($path, $view, $vars = array(), $return = FALSE)
{
$full_path = $path.$view.'.php';
if (file_exists($full_path))
{
return $this->_ci_load(array('_ci_path' => $full_path, '_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
}
else
{
show_error('Unable to load the requested module template file: '.$view);
}
}
and you can work the rest from the controller.
I do this in my controller:
public function index()
{
if($this->agent->is_mobile())
{
$this->load_mobile();
}
else
{
$this->load_web();
}
}
public function load_mobile()
{
$this->load->view('mobile/home');
}
public function load_web()
{
$this->load->view('web/home');
}
In this way I can add different data to mobile and to web pages.
I also extend the default controller and add some useful extra features:
Enables the usage of master page/templates.
Can add css and javascript files.
Uses the _output method for controlling the controllers output.
Can load relative content with in the form of modules (views)
So I can manage better the different pages.
Bye!!

Running a Zend Framework action from command line

I would like to run a Zend Framework action to generate some files, from command line. Is this possible and how much change would I need to make to my existing Web project that is using ZF?
Thanks!
UPDATE
You can have all this code adapted for ZF 1.12 from https://github.com/akond/zf-cli if you like.
While the solution #1 is ok, sometimes you want something more elaborate.
Especially if you are expecting to have more than just one CLI script.
If you allow me, I would propose another solution.
First of all, have in your Bootstrap.php
protected function _initRouter ()
{
if (PHP_SAPI == 'cli')
{
$this->bootstrap ('frontcontroller');
$front = $this->getResource('frontcontroller');
$front->setRouter (new Application_Router_Cli ());
$front->setRequest (new Zend_Controller_Request_Simple ());
}
}
This method will deprive dispatching control from default router in favour of our own router Application_Router_Cli.
Incidentally, if you have defined your own routes in _initRoutes for your web interface, you would probably want to neutralize them when in command-line mode.
protected function _initRoutes ()
{
$router = Zend_Controller_Front::getInstance ()->getRouter ();
if ($router instanceof Zend_Controller_Router_Rewrite)
{
// put your web-interface routes here, so they do not interfere
}
}
Class Application_Router_Cli (I assume you have autoload switched on for Application prefix) may look like:
class Application_Router_Cli extends Zend_Controller_Router_Abstract
{
public function route (Zend_Controller_Request_Abstract $dispatcher)
{
$getopt = new Zend_Console_Getopt (array ());
$arguments = $getopt->getRemainingArgs ();
if ($arguments)
{
$command = array_shift ($arguments);
if (! preg_match ('~\W~', $command))
{
$dispatcher->setControllerName ($command);
$dispatcher->setActionName ('cli');
unset ($_SERVER ['argv'] [1]);
return $dispatcher;
}
echo "Invalid command.\n", exit;
}
echo "No command given.\n", exit;
}
public function assemble ($userParams, $name = null, $reset = false, $encode = true)
{
echo "Not implemented\n", exit;
}
}
Now you can simply run your application by executing
php index.php backup
In this case cliAction method in BackupController controller will be called.
class BackupController extends Zend_Controller_Action
{
function cliAction ()
{
print "I'm here.\n";
}
}
You can even go ahead and modify Application_Router_Cli class so that not "cli" action is taken every time, but something that user have chosen through an additional parameter.
And one last thing. Define custom error handler for command-line interface so you won't be seeing any html code on your screen
In Bootstrap.php
protected function _initError ()
{
$error = $frontcontroller->getPlugin ('Zend_Controller_Plugin_ErrorHandler');
$error->setErrorHandlerController ('index');
if (PHP_SAPI == 'cli')
{
$error->setErrorHandlerController ('error');
$error->setErrorHandlerAction ('cli');
}
}
In ErrorController.php
function cliAction ()
{
$this->_helper->viewRenderer->setNoRender (true);
foreach ($this->_getParam ('error_handler') as $error)
{
if ($error instanceof Exception)
{
print $error->getMessage () . "\n";
}
}
}
It's actually much easier than you might think. The bootstrap/application components and your existing configs can be reused with CLI scripts, while avoiding the MVC stack and unnecessary weight that is invoked in a HTTP request. This is one advantage to not using wget.
Start your script as your would your public index.php:
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV',
(getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
: 'production'));
require_once 'Zend/Application.php';
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/config.php'
);
//only load resources we need for script, in this case db and mail
$application->getBootstrap()->bootstrap(array('db', 'mail'));
You can then proceed to use ZF resources just as you would in an MVC application:
$db = $application->getBootstrap()->getResource('db');
$row = $db->fetchRow('SELECT * FROM something');
If you wish to add configurable arguments to your CLI script, take a look at Zend_Console_Getopt
If you find that you have common code that you also call in MVC applications, look at wrapping it up in an object and calling that object's methods from both the MVC and the command line applications. This is general good practice.
Just saw this one get tagged in my CP. If you stumbled onto this post and are using ZF2, it's gotten MUCH easier. Just edit your module.config.php's routes like so:
/**
* Router
*/
'router' => array(
'routes' => array(
// .. these are your normal web routes, look further down
),
),
/**
* Console Routes
*/
'console' => array(
'router' => array(
'routes' => array(
/* Sample Route */
'do-cli' => array(
'options' => array(
'route' => 'do cli',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'do-cli',
),
),
),
),
),
),
Using the config above, you would define doCliAction in your IndexController.php under your Application module. Running it is cake, from the command line:
php index.php do cli
Done!
Way smoother.
akond's solution above is on the best track, but there are some subtleties that may may his script not work in your environment. Consider these tweaks to his answer:
Bootstrap.php
protected function _initRouter()
{
if( PHP_SAPI == 'cli' )
{
$this->bootstrap( 'FrontController' );
$front = $this->getResource( 'FrontController' );
$front->setParam('disableOutputBuffering', true);
$front->setRouter( new Application_Router_Cli() );
$front->setRequest( new Zend_Controller_Request_Simple() );
}
}
Init error would probably barf as written above, the error handler is probably not yet instantiated unless you've changed the default config.
protected function _initError ()
{
$this->bootstrap( 'FrontController' );
$front = $this->getResource( 'FrontController' );
$front->registerPlugin( new Zend_Controller_Plugin_ErrorHandler() );
$error = $front->getPlugin ('Zend_Controller_Plugin_ErrorHandler');
$error->setErrorHandlerController('index');
if (PHP_SAPI == 'cli')
{
$error->setErrorHandlerController ('error');
$error->setErrorHandlerAction ('cli');
}
}
You probably, also, want to munge more than one parameter from the command line, here's a basic example:
class Application_Router_Cli extends Zend_Controller_Router_Abstract
{
public function route (Zend_Controller_Request_Abstract $dispatcher)
{
$getopt = new Zend_Console_Getopt (array ());
$arguments = $getopt->getRemainingArgs();
if ($arguments)
{
$command = array_shift( $arguments );
$action = array_shift( $arguments );
if(!preg_match ('~\W~', $command) )
{
$dispatcher->setControllerName( $command );
$dispatcher->setActionName( $action );
$dispatcher->setParams( $arguments );
return $dispatcher;
}
echo "Invalid command.\n", exit;
}
echo "No command given.\n", exit;
}
public function assemble ($userParams, $name = null, $reset = false, $encode = true)
{
echo "Not implemented\n", exit;
}
}
Lastly, in your controller, the action that you invoke make use of the params that were orphaned by the removal of the controller and action by the CLI router:
public function echoAction()
{
// disable rendering as required
$database_name = $this->getRequest()->getParam(0);
$udata = array();
if( ($udata = $this->getRequest()->getParam( 1 )) )
$udata = explode( ",", $udata );
echo $database_name;
var_dump( $udata );
}
You could then invoke your CLI command with:
php index.php Controller Action ....
For example, as above:
php index.php Controller echo database123 this,becomes,an,array
You'll want to implement a more robust filtering/escaping, but, it's a quick building block. Hope this helps!
One option is that you could fudge it by doing a wget on the URL that you use to invoke the desirable action
You cant use -O option of wget to save the output. But wget is clearly NOT the solution. Prefer using CLI instead.
akond idea works great, except the error exception isnt rendered by the error controller.
public function cliAction() {
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
foreach ($this->_getParam('error_handler') as $error) {
if ($error instanceof Exception) {
print "cli-error: " . $error->getMessage() . "\n";
}
}
}
and In Application_Router_Cli, comment off the echo and die statement
public function assemble($userParams, $name = null, $reset = false, $encode = true) {
//echo "Not implemented\n";
}
You can just use PHP as you would normally from the command line. If you call a script from PHP and either set the action in your script you can then run whatever you want.
It would be quite simple really.
Its not really the intended usage, however this is how it could work if you wanted to.
For example
php script.php
Read here: http://php.net/manual/en/features.commandline.php
You can use wget command if your OS is Linux. For example:
wget http://example.com/controller/action
See http://linux.about.com/od/commands/l/blcmdl1_wget.htm
UPDATE:
You could write a simple bash script like this:
if wget http://example.com/controller/action
echo "Hello World!" > /home/wasdownloaded.txt
else
"crap, wget timed out, let's remove the file."
rm /home/wasdownloaded.txt
fi
Then you can do in PHP:
if (true === file_exists('/home/wasdownloaded.txt') {
// to check that the
}
Hope this helps.
I have used wget command
wget http://example.com/module/controller/action -O /dev/null
-O /dev/null if you dont want to save the output

Categories