ZF2 Zend\Console routes don't match in windows command prompt - php

I have set up a couple of console routes in a Zend Framework 2 project to test out, and while it works on linux (ubuntu 12.10) , it won't work on my windows 7 machine using command prompt or cygwin. I just get the following error message:
Reason for failure: Invalid arguments or no arguments provided
In my module.config.php file, I have
'console' => array(
'router' => array(
'routes' => array(
'some-route' => array(
'options' => array(
'route' => 'some route',
'defaults' => array(
'controller' => 'Application\Controller\Cron',
'action' => 'some-route'
)
)
),
'another-route' => array(
'options' => array(
'route' => 'another route',
'defaults' => array(
'controller' => 'Application\Controller\Cron',
'action' => 'another-route'
)
)
)
)
)
)
And in my controller
class CronController extends AbstractActionController
{
public function someRouteAction()
{
return 'some route called' . PHP_EOL;
}
public function anotherRouteAction()
{
return 'some other route called' . PHP_EOL;
}
}
Does anyone know of a reason why the exact same code works on ubuntu but not on windows?

1- You can check the router factory on Zend/Mvc/Service/RouterFactory.php
 to make sure that your console routing configuration is not in cache.
public function createService(ServiceLocatorInterface $serviceLocator, $cName = null, $rName = null)
{
$config = $serviceLocator->get('Configuration') ;
//#ToDo Remove
print_r($config);
if(
$rName === 'ConsoleRouter' ||
($cName === 'router' && Console::isConsole())
) {
if(isset($config['console']) && isset($config['console']['router'])){
$routerConfig = $config['console']['router'] ;
} else {
$routerConfig = array();
}
$router = ConsoleRouter::factory($routerConfig) ;
} else {
[…]
}
return $router ;
}
Alternatively it is necessary to verify in application.config.php if config_cache_enabled is true

Related

url parameter becomes null after clicking button

Here is my route:
'router' => array(
'routes' => array(
'upload' => array(
'type' => 'segment',
'options' => array(
'route' => '/products/upload[/:products]',
'defaults' => array(
'controller' => 'Products\Controller\Upload',
'action' => 'index'
),
),
'may_terminate' => true,
'child_routes' => array(
'uploadsuccessful' => array(
'type' => 'literal',
'options' => array(
'route' => '/uploadsuccessful',
'defaults' => array(
'controller' => 'Products\Controller\Upload',
'action' => 'successful'
),
),
),
),
),
),
);
I am trying to call this route several times from different view scripts giving different [/:products] parameter.
Upload Shoes Product Image
Upload Trainers Product Image
Upload Hat Product Image
Here is my Controller code.
<?php
namespace Products\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Products\Form\UploadForm;
class UploadController extends AbstractActionController
{
protected $_dir = null;
public function indexAction()
{
$products = $this->params()->fromRoute('products');
$config = $this->getServiceLocator()->get('Config');
$fileManagerDir =$config['file_manager']['dir'];
$this->_dir = realpath($fileManagerDir) .
DIRECTORY_SEPARATOR .
$products;
if (!is_dir($this->_dir)) {
//read, write, execute
mkdir($this->_dir, 0777);
}
$form = new UploadForm($this->_dir, 'upload-form');
$request = $this->getRequest();
if ($request->isPost()) {
$post = array_merge_recursive(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($post);
if ($form->isValid()) {
$data = $form->getData();
$this->setFileNames($data);
return $this->redirect()->toRoute('upload/uploadsuccessful', array('products' =>$products));
}
}
return new ViewModel(array('form' => $form));
}
public function successfulAction()
{
$file = array();
$flashMessenger = $this->flashMessenger();
if ($flashMessenger->hasMessages()) {
foreach($flashMessenger->getMessages() as $key => $value) {
$file = $value;
}
}
return new ViewModel(array('file' => $file));
}
protected function setFileNames($data)
{
unset($data['submit']);
foreach ($data['image-file'] as $key => $file) {
rename($file['tmp_name'], $this->_dir . DIRECTORY_SEPARATOR . $file['name']);
}
}
}
I think the idea is clear: for each [/:products] parameter I tried to make separate folder with given name in $fileManagerDir.
But, there is a problem. When I click on button upload ($request->isPost() == true) parameter $products becomes null and uploaded files don't go to appropriate folders. Also I am not able to redirect to successful action - the error appears "missing parameter" because $products is null.
In your view script the upload buttons are href links. This will result in a GET REQUEST being sent to the controller and not a POST and no FILES will be present either. Unless that is, you are intercepting the click event with external JS which you haven't mentioned here. The route url's should be in the form action attribute. The submit buttons should be buttons not links (unless you are using JS not mentioned here). Make sure the form method is set to POST.
Something like:
<form action="<?php echo $this->url('upload', array('products' =>'shoes')); ?>" method="post" enctype="multipart/form-data">
You'll need to deal with the dynamic action using JS or redesign your routing.

ZF2 Console routing doesn't run well

I used Zend Framework version 2. I follow the instruction from here but my project doesn't run well.
Here's my code :
module.config.php
return array(
'controllers'=> array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController',
),
),
'router' => array(
'routes' => array(
)
),
// Placeholder for console routes
'console' => array(
'router' => array(
'routes' => array(
'list-users' => array(
'options' => array(
'route' => 'show [all|disabled|deleted]:mode users [--verbose|-v]',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'showusers'
)
)
),
),
),
),
);
Module.php
public function getConsoleUsage(Console $console)
{
return array(
'Finding and listing users',
'list [all|disabled] users [-w]' => 'Show a list of users',
'find user [--email=] [--name=]' => 'Attempt to find a user by email or name',
array('[all|disabled]', 'Display all users or only disabled accounts'),
array('--email=EMAIL', 'Email of the user to find'),
array('--name=NAME', 'Full name of the user to find.'),
array('-w', 'Wide output - When listing users use the whole available screen width' ),
'Manipulation of user database:',
'delete user <userEmail> [--verbose|-v] [--quick]' => 'Delete user with email <userEmail>',
'disable user <userEmail> [--verbose|-v]' => 'Disable user with email <userEmail>',
array( '<userEmail>' , 'user email' , 'Full email address of the user to change.' ),
array( '--verbose' , 'verbose mode' , 'Display additional information during processing' ),
array( '--quick' , '"quick" operation' , 'Do not check integrity, just make changes and finish' ),
array( '-v' , 'Same as --verbose' , 'Display additional information during processing' ),
);
}
IndexController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return new ViewModel();
}
public function showusersAction(){
$request = $this->getRequest();
return "HOLLLLLLLLLLLLAAAAAAAAAAAA"; // show it in the console
}
}
When i tried to run it using command promt :
C:\webserver\www\program\phpcas\zf2-console>php public/index.php show users
Always showing getConsoleUsage() from Module.php, can anyone help me, how to fix it ?
From my (little) experience, it seems that your module.config.php and your Module.php files are correct.
Usually, when I use the console, my controllers are slightly different :
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
// For Cron/Console
use Zend\Console\Request as ConsoleRequest;
use Zend\Console\ColorInterface as Color;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return new ViewModel();
}
public function showusersAction()
{
// Initialize variables.
$request = $this->getRequest();
$console = $this->getServiceLocator()->get('console');
// Make sure that we are running in a console and the user has not tricked our
// application into running this action from a public web server.
if (!$request instanceof ConsoleRequest) {
throw new \RuntimeException('You can only use this action from a console!');
}
// Retrieve values from value flags using their name.
$verbose = (bool)$request->getParam('verbose', false) || (bool)$request->getParam('v', false); // default false
/* ...your other flags... */
/* ...do what you want to do... */
// Let's confirm it works
$console->writeline("Groovy Baby!");

How can I create a URL in a Console Controller in ZF2?

I have a Console Controller and an action to send emails (defined below in module.config.php)
'console' => array(
'router' => array(
'routes' => array(
'cronroute' => array(
'options' => array(
'route' => 'sendEmails',
'defaults' => array(
'controller' => 'Application\Controller\Console',
'action' => 'send-emails'
)
)
),
)
)
),
In the action I want to send an email that contains a link to another action on the site. This would normally be done with a URL View Helper, but since the Request is of type Console and not HTTP, that doesn't work. I've tried to create an HTTP request, but I do not know how to give it the site domain or the Controller/Action link.
My Controller code:
$vhm = $this->getServiceLocator()->get('viewhelpermanager');
$url = $vhm->get('url');
$urlString = $url('communication', array('action' => 'respond', 'id' => $id,
array('force_canonical' => true));
This throws an error:
======================================================================
The application has thrown an exception!
======================================================================
Zend\Mvc\Router\Exception\RuntimeException
Request URI has not been set
How do I create an HTTP Request in a Console Controller that has the site scheme, domain and path/to/action? And how do I pass it along to the URL View Helper?
Here's how this issue can be solved:
<?php
// Module.php
use Zend\View\Helper\ServerUrl;
use Zend\View\Helper\Url as UrlHelper;
use Zend\Uri\Http as HttpUri;
use Zend\Console\Console;
use Zend\ModuleManager\Feature\ViewHelperProviderInterface;
class Module implements ViewHelperProviderInterface
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
'url' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$viewHelper = new UrlHelper();
$routerName = Console::isConsole() ? 'HttpRouter' : 'Router';
/** #var \Zend\Mvc\Router\Http\TreeRouteStack $router */
$router = $serviceLocator->get($routerName);
if (Console::isConsole()) {
$requestUri = new HttpUri();
$requestUri->setHost($config['website']['host'])
->setScheme($config['website']['scheme']);
$router->setRequestUri($requestUri);
}
$viewHelper->setRouter($router);
$match = $serviceLocator->get('application')
->getMvcEvent()
->getRouteMatch();
if ($match instanceof RouteMatch) {
$viewHelper->setRouteMatch($match);
}
return $viewHelper;
},
'serverUrl' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$serverUrlHelper = new ServerUrl();
if (Console::isConsole()) {
$serverUrlHelper->setHost($config['website']['host'])
->setScheme($config['website']['scheme']);
}
return $serverUrlHelper;
},
),
);
}
}
Of course you have to define default host and scheme values in config since there's no way to detect them automatically in console mode.
Update:
The correct answer for this post can be found here:
Stackoverflow: Using HTTP routes within ZF2 console application
Well you're very close to this but you are not using the Url plugin. If you dived a little bit further into the ZF2 Documentation of the Controller Plugins you could have found the solution.
See for reference: ZF2 Documentation - Controller plugins
Your ConsoleController has to implement one of the following, to be able to retrieve the Controller plugins:
AbstractActionController
AbstractRestfulController
setPluginManager
Well I recommend to extend your controller with the AbstractActionController if you haven't done it yet.
In case you do use the AbstractActionController you can simply call $urlPlugin = $this->url() since the AbstractActionController has an __call() implementation retrieving the plugin for you. But you can also use: $urlPlugin = $this->plugin('url');
So in order to generate the URL for your mail you can do the following in your controller:
$urlPlugin = $this->url();
$urlString = $urlPlugin->fromRoute(
'route/myroute',
array(
'param1' => $param1,
'param2' => $param2
),
array(
'option1' => $option1,
'option2' => $option2
)
);
You can now pass this URL to your viewModel or use the URL viewHelper within your viewModel, but that is up to you.
Try to avoid viewHelpers within your controller as we've got plugins available for this case.
In case you wonder what methods the AbstractActionController has, here is ZF2 ApiDoc - AbstractActionController
In order to make this work you have to setup your route config with a proper structure:
// This can sit inside of modules/Application/config/module.config.php or any other module's config.
array(
'router' => array(
'routes' => array(
// HTTP routes are here
)
),
'console' => array(
'router' => array(
'routes' => array(
// Console routes go here
)
)
),
)
If you've got a Console module, just stick with the console route paths. Don't forget the console key with all the routes beneath it! Take a look at the documentation for a reference: ZF2 - Documentation: Console routes and routing
I think the best solution is using a DelegatorFactory.
config/autoload/server-url.local.php:
return [
'server_url' => 'http://example.com',
];
module/Application/config/module.config.php:
'service_manager' => [
'delegators' => [
TreeRouteStack::class => [
TreeRouteStackConsoleDelegatorFactory::class,
],
]
],
module/Application/src/TreeRouteStackConsoleDelegatorFactory.php:
namespace Application;
use Interop\Container\ContainerInterface;
use Zend\Router\Http\TreeRouteStack;
use Zend\ServiceManager\Factory\DelegatorFactoryInterface;
use Zend\Uri\Http;
class TreeRouteStackConsoleDelegatorFactory implements DelegatorFactoryInterface
{
public function __invoke(ContainerInterface $container, $name, callable $callback, array $options = null)
{
/** #var TreeRouteStack $treeRouteStack */
$treeRouteStack = $callback();
if (!$treeRouteStack->getRequestUri()) {
$treeRouteStack->setRequestUri(
new Http($container->get('config')['server_url'])
);
}
return $treeRouteStack;
}
}
I can't believe it but I did it :)
Hope it will work for all of You.
In controller where the fromRoute() function is used I added those lines below:
$event = $this->getEvent();
$http = $this->getServiceLocator()->get('HttpRouter');
$router = $event->setRouter($http);
$request = new \Zend\Http\Request();
$request->setUri('');
$router = $event->getRouter();
$routeMatch = $router->match($request);
var_dump($this->url()->fromRoute(
'route_parent/route_child',
[
'param1' => 1,
'param2' => 2,
)
);
Output:
//mydomain.local/route-url/1/2
Of course route_parent/route_child is not a console route but HTTP route :)
Thank #Alexey Kosov for response. You will probably have an issue when your application is working under subdirectory rather than root directory after domain '/'.
You have to add:
$router->setBaseUrl($config['website']['path']);
Whole code:
<?php
// Module.php
use Zend\View\Helper\ServerUrl;
use Zend\View\Helper\Url as UrlHelper;
use Zend\Uri\Http as HttpUri;
use Zend\Console\Console;
use Zend\ModuleManager\Feature\ViewHelperProviderInterface;
class Module implements ViewHelperProviderInterface
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
'url' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$viewHelper = new UrlHelper();
$routerName = Console::isConsole() ? 'HttpRouter' : 'Router';
/** #var \Zend\Mvc\Router\Http\TreeRouteStack $router */
$router = $serviceLocator->get($routerName);
if (Console::isConsole()) {
$requestUri = new HttpUri();
$requestUri->setHost($config['website']['host'])
->setScheme($config['website']['scheme']);
$router->setRequestUri($requestUri);
$router->setBaseUrl($config['website']['path']);
}
$viewHelper->setRouter($router);
$match = $serviceLocator->get('application')
->getMvcEvent()
->getRouteMatch();
if ($match instanceof RouteMatch) {
$viewHelper->setRouteMatch($match);
}
return $viewHelper;
},
'serverUrl' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$serverUrlHelper = new ServerUrl();
if (Console::isConsole()) {
$serverUrlHelper->setHost($config['website']['host'])
->setScheme($config['website']['scheme']);
}
return $serverUrlHelper;
},
),
);
}
}
I had an similar problem with zend console - the serverUrl view helper also not worked properly by default.
My case:
/module/Application/src/Application/Controller/ConsoleController.php
...
public function someAction()
{
...
$view = new ViewModel([
'data' => $data,
]);
$view->setTemplate('Application/view/application/emails/some_email_template');
$this->mailerZF2()->send(array(
'to' => $data['customer_email'],
'subject' => 'Some email subject',
), $view);
...
}
/module/Application/view/application/emails/some_email_template.phtml
<?php
/** #var \Zend\View\Renderer\PhpRenderer $this */
/** #var array $data */
?><!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link ... rel="stylesheet" />
<title>...</title>
</head>
<body>
<div style="...">
... <img src="<?= $this->serverUrl() ?>/images/logo-maillist.png" width="228" height="65"> ...
<p>Hello, <?= $this->escapeHtml($data['customer_name']) ?>!</p>
<p>... email body ...</p>
<div style="...">
some action ...
</div>
...
</div>
</body>
</html>
The serverUrl view helper returns just only "http://" under Console Controller (runned by cron). But the same template renders properly under web http requests handled by other controllers.
I fixed it by this way:
/config/autoload/global.php
return array(
...
'website' => [
'host' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'my.production.domain',
'scheme' => 'https',
'path' => '',
],
);
/config/autoload/local.php
return array(
...
'website' => [
'host' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'my.local.domain',
],
);
/public/index.php (the ZF2 engine starting script)
chdir(dirname(__DIR__));
// --- Here is my additional code ---------------------------
if (empty($_SERVER['HTTP_HOST'])) {
function array_merge_recursive_distinct(array &$array1, array &$array2)
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
$basicConfig = require 'config/autoload/global.php';
$localConfig = #include 'config/autoload/local.php';
if (!empty($localConfig)) {
$basicConfig = array_merge_recursive_distinct($basicConfig, $localConfig);
}
unset($localConfig);
$_SERVER['HTTP_HOST'] = $basicConfig['website']['host'];
$_SERVER['HTTP_SCHEME'] = $basicConfig['website']['scheme'];
$_SERVER['HTTPS'] = $_SERVER['HTTP_SCHEME'] === 'https' ? 'on' : '';
$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
unset($basicConfig);
}
// ---/End of my additional code ---------------------------
// Setup autoloading
require 'init_autoloader.php';
...
And that's all that I changed.
Magic! It works! :-)
Hope this helps to someone too.

zend-fzend framework 2 cron

I have application created in Zend Framework 2. I would like to run controller action as cron job. I read a lot about it but I don't know how to run it.
I did something like that. I added console route to module's module.config.php
'console' => array(
'router' => array(
'routes' => array(
'cronroute' => array(
'options' => array(
'route' => 'updateproducts',
'defaults' => array(
'controller' => 'Application\Controller\Console',
'action' => 'update'
)
)
)
),
),
),
I created Controller named ConsoleController
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Model;
use Application\Model\Auth;
class ConsoleController extends AbstractActionController
{
protected $usersTable = null;
public function updateAction()
{
$this->getUsersTable()->cronTest();
return false;
}
public function getUsersTable()
{
if (!$this->usersTable) {
$sm = $this->getServiceLocator();
$this->usersTable = $sm->get('Application\Model\UsersTable');
}
return $this->usersTable;
}
Inside Auth.php
public function cronTest(){
$data = array(
'login' => 'cronZend'
);
$this->tableGateway->insert($data);
}
I think that everything here is OK and I tried to write something inside command line on shared server:
/usr/local/bin/php /home/****/domains/****/public_html/public/index.php updateproducts
and it doesn't work. When I test it writing something like that in cron.php
$dbc = mysqli_connect(****);
$query = "INSERT INTO vm_user (login) VALUES ('cron')";
mysqli_query($dbc, $query);
and in command line
/usr/local/bin/php /home/****/domains/****/public_html/cron.php
it works fine. Could anyone tell me what is wrong or how to do it?

Magento Grid : Better way of using addColumn Actions?

This function sets up the Magento Grid to display a list of filenames with a corresponding 'Delete' action.
The problem is the Delete action never passes the parameter, 'filename.' (See http://www.premasolutions.com/content/magento-manage-category-product-grid-edit-link) I have TESTDUMP for verification but it never prints on the next page.
Is 'params' a legitimate action of 'addColumn->actions->url'?
Update: Added the construct and prepare collection for controller. Maybe it's because of the type of Collection I'm using?
class Rogue_Googlemerchant_Block_Adminhtml_Exporter_Grid
Rogue_Googlemerchant_Block_Adminhtml_Exporter_Grid extends Mage_Adminhtml_Block_Widget_Grid
{
public function __construct()
{
parent::__construct();
$this->setId('googlemerchantGrid');
// This is the primary key of the database
$this->setDefaultSort('filename');
$this->setDefaultDir('ASC');
$this->setSaveParametersInSession(true);
}
protected function _prepareCollection()
{
$basePath = Mage::getBaseDir('base');
$feedPath = $basePath . '/opt/googlemerchant/';
$errPath = $basePath . '/var/log/googlemerchant/';
$flocal = new Varien_Io_File();
$flocal->open(array('path' => $feedPath));
$dataCollection = new Varien_Data_Collection();
foreach ($flocal->ls() as $item) {
$dataObject = new Varien_Object();
$dataObject->addData(
array(
'filename' => $item['text'],
'size' => $item['size'] / 1000 . ' kb',
'date_modified'=> $item['mod_date']
)
);
$dataCollection->addItem($dataObject);
}
$this->setCollection($dataCollection);
return parent::_prepareCollection();
}
protected function _prepareColumns()
{
$this->addColumn('filename', array(
'header' => Mage::helper('googlemerchant')->__('File'),
'align' =>'left',
'index' => 'filename',
'width' => '200px',
));
$this->addColumn('action', array(
'header' => Mage::helper('googlemerchant')->__('Action'),
'width' => '50px',
'type' => 'action',
// 'getter' => 'getId',
'actions' => array(
array(
'caption' => Mage::helper('googlemerchant')->__('Delete'),
'url' =>
array(
'base' => '*/*/delete',
'params' => array('filename' => 'TESTDUMP')
),
'field' => 'filename'
)
),
'filter' => false,
'sortable' => false,
// 'index' => 'filename',
// 'is_system' => true,
));
}
}
class Rogue_Googlemerchant_Adminhtml_ExporterController
class Rogue_Googlemerchant_Adminhtml_ExporterController extends Mage_Adminhtml_Controller_Action
{
public function deleteAction()
{
$filename = $this->getRequest()->getParam('filename');
$basePath = Mage::getBaseDir('base');
$feedPath = $basePath . '/opt/googlemerchant/';
$errPath = $basePath . '/var/log/googlemerchant/';
$flocal = new Varien_Io_File();
$flocal->open(array('path' => $feedPath));
d($filename);
if ($filename) {
try{
$flocal->rm($filename);
Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('googlemerchant')->__('The file has been deleted.'));
$this->_redirect('*/*/');
}
catch (Mage_Core_Exception $e) {
$this->log($e);
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
$this->_redirect('*/*/index');
return;
}
}
die('here');
Mage::getSingleton('adminhtml/session')->addError(Mage::helper('adminhtml')->__('Unable to find the file to delete.'));
$this->_redirect('*/*/');
}
}
The getter of the action column is used on the collection items to retrieve the argument value for the field parameter.
I'm not sure why you are specifying the filename hardcoded or if that should work, but if you add the column configuration
'getter' => 'getFilename'
and remove the params from the action it should work.

Categories