I want to implement custom routes in CakePHP. I am following the docs
http://book.cakephp.org/2.0/en/development/routing.html#custom-route-classes
My custom route in app/Routing/Route
<?php
App::uses('CakeRoute', 'lib/Cake/Routing/Route');
class CategoryRoute extends CakeRoute
{
public function parse($url)
{
$params = parent::parse($url);
if (empty($params)) {
return false;
}
return true;
}
}
app/Config/routes.php
App::uses('CategoryRoute', 'Routing/Route');
Router::connect('/mypage/*', array('controller' => 'mycontroller', 'action' => 'view'), ['routeClass' => 'CategoryRoute']);
but I get
Missing Controller
Error: Controller could not be found.
Error: Create the class Controller below in file: app/Controller/Controller.php
When I remove ['routeClass' => 'CategoryRoute'], rerouting just works fine.
Have a closer look at the API documenation: API > CakeRoute::parse()
The parse() method is supposed to return an array of parsed parameters (ie $params) on success, or false on failure.
Related
I am using Cakephp 3.x in which I have created one method inside my AppController (checkBrandAssigned) in which I have checked my logic and redirecting to the other page as per my needs. Below is how method looks like.
AppController.php
public function checkBrandAssigned(){
$this->loadModel('Users');
$user_data = $this->Users->find()
->select(['user_id'])
->where([
'user_type' => SALES_REPRESENTETIVE,
'user_status' => STATUS_INACTIVE,
'user_id NOT IN (SELECT ub_user_id FROM fp_user_brand)'
])
->order([ 'user_modified_date'=>'DESC'])
->first();
if (!empty($user_data)) {
return $user_data->user_id;
} else {
return '';
}
}
And below is my full snippet of AppController.php file.
<?php
class AppController extends Controller {
public function initialize() {
parent::initialize();
switch ($this->request->session()->read('Auth.User.user_type')):
case COMPANY_ADMIN :
$loginRedirectUrl = ['controller' => 'users', 'action' => 'dashboardCompanyAdmin'];
break;
default : $loginRedirectUrl = ['controller' => 'users', 'action' => 'dashboardSalesRep'];
break;
endswitch;
}
public function checkBrandAssigned(){
$this->loadModel('Users');
$user_data = $this->Users->find()
->select(['user_id'])
->where([
'user_type' => SALES_REPRESENTETIVE,
'user_status' => STATUS_INACTIVE,
'user_id NOT IN (SELECT ub_user_id FROM fp_user_brand)'
])
->order([ 'user_modified_date'=>'DESC'])
->first();
if (!empty($user_data)) {
return $user_data->user_id;
} else {
return '';
}
}
}
As you can see above, I have used checkBrandAssigned method inside my beforeFilter and if my condition gets true, I am redirecting at, $this->redirect(['controller' => 'Users', 'action' => 'brandRetailerView','?'=>['repId'=>'6b12337d37870e4afb708a20f4f127af']]);.
Below is how my UsersController.php file.
UsersController.php
class UsersController extends AppController {
public function initialize() {
parent::initialize();
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
}
public function brandRetailerView() {
die('inside my function');
}
}
As you can see I have put die('inside my function'); I am able to see this text, howerver, If I comment it, I am getting error saying...
The page isn’t redirecting properly
I have also created blank brand_retailer_view.ctp file in my respective location. Below is screenshot of my error.
I have also used Auth Component. I want brandRetailerView and checkBrandAssigned methods to be used after login hence I have not added inside my $this->Auth->allow in AppController.php and UsersController.php. However, I tried to add checkBrandAssigned in $this->Auth->allow, but still its not working.
I have tried to use return parent::beforeFilter($event); in my UsersController.php but its not working as well.
I have not added anything extra inside my .htaccess file, hence I am not adding here.
Can someone help me why I am having this issue. Even if I use die its working but as soon as I comment it, I am getting above error.
the issue is not in the code, it's in the logic
the issue is because irrespective of the condition returned by checkBrandAssigned(), the redirect will be to users controller and before filtering the user controller the condition will be checked again and will again be redirected to the user controller.
it will become an infinite loop of redirection, in case of die() it is working because die() break the loop before the redirect happens.
to solve this remove the redirect check from beforeFillter and put somewhere else...
add this to enable cookies in AppController.
use Cake\Controller\Component\CookieComponent;
and update the condition as follows.
if (!isset($this->Cookie->read('checkedInLast30Secs'))) {
$this->Cookie->write('checkedInLast30Secs',
[
'value' => true,
'expires' => '+30 secs'
]
);
if (!empty($checkSalesRepId)) {
$this->redirect(['controller' => 'Users', 'action' => 'brandRetailerView', '?' => ['repId' => '6b12337d37870e4afb708a20f4f127af']]);
// $this->dashboardUrl = ['controller' => 'users', 'action' => 'brandRetailerView'];
} else {
$this->dashboardUrl = ['controller' => 'users', 'action' => 'dashboardCompanyAdmin'];
}
}
Eventually I have solved with below code.
case COMPANY_ADMIN :
if (!empty($checkSalesRepId)) {
if($this->request->params['action'] != 'brandRetailerView'){
$this->redirect(['controller' => 'Users', 'action' => 'brandRetailerView','?'=>['repId'=>$checkSalesRepId]]);
}
else {
$this->dashboardUrl = ['controller' => 'users', 'action' => 'dashboardSalesManager'];
}
}
Here I am simply checking if the current action is not brandRetailerView. If so, I am redirecting to brandRetailerView page, else dashboardSalesManager page.
Hope this helps.
When a user visits a particular url in my yii 2.0 application without required parameters, I want to present a form to collect the required missing parameters.
for this purpose, I need the names of missing parameters, e.g. I have a function
public function actionBlast ($bomb, $building) {
}
I expect the results as an array like this
$args = [0=>'bomb', 1=>'building'];
I tried func_get_args() but it returns null, and the undocumented ReflectionFunctionAbstract::getParameters ( void ) etc. Any other way out?
I think the best way to achieve what you want is to override the default ErrorAction.
Inside your controllers directory, create:
controllers
actions
ErrorAction.php
In ErrorAction.php, add:
<?php
namespace frontend\controllers\actions;
use Yii;
use yii\web\ErrorAction as DefaultErrorAction;
class ErrorAction extends DefaultErrorAction
{
public function run()
{
$missing_msg = 'Missing required parameters:';
$exception = Yii::$app->getErrorHandler()->exception;
if (substr($exception->getMessage(), 0, strlen($missing_msg)) === $missing_msg) {
$parameters = explode(',', substr($exception->getMessage(), strlen($missing_msg)));
return $this->controller->render('missing_params_form' ?: $this->id, [
'parameters' => $parameters,
]);
}
return parent::run();
}
}
In your controller add:
public function actions()
{
return [
'error' => [
'class' => 'frontend\controllers\actions\ErrorAction',
],
];
}
and create a view "missing_params_form.php" in your controller `s view directory, where you can generate your form fields.
I believe this to be your best option, though you may need to update it in case a Yii update changes the error message.
I have a controller in Laravel 5.
I would like to write a controller function that accepts variable arguments.
For example,
public function show(Request $request, ...$id)
{
// handle multiple $id values here
}
The reason is that I have a url structure that has 'nested' models.
For instance:
\item\{$id}
\parent\{$parent_id}\item\{$id}
\grandparent\{$grandparent_id}\parent\{$parent_id}\item\{$id}
The routes are defined as:
Route::resource('item', 'ItemController');
Route::resource('parent.item', 'ParentController');
Route::resource('grandparent.parent.item', 'GrandparentController');
My desire is to write a single show() method as a trait that each controller can use.
Because of the structure of my database, it is possible.
But the UrlGenerator keeps throwing a UrlGenerationException when I try to use variable arguments. It seems like it doesn't understand this construct?
Ok, here's an idea for you that should get you on the right path:
For the various resource routes you defined, re-declare them to exclude the 'show' action, and define a separate GET route to map the routes you are trying to centralise.
app/Http/routes.php:
Route::resource('item', 'ItemController', ['except' => ['show']]);
Route::get('item/{item}', ['uses' => 'AggregateController#handleShow', 'as' => 'item.show']);
Route::resource('parent.item', 'ParentController', ['except' => ['show']]);
Route::get('parent/{parent}/item/{item}', ['uses' => 'AggregateController#handleShow', 'as' => 'parent.item.show']);
Route::resource('grandparent.parent.item', 'GrandParentController', ['except' => ['show']]);
Route::get('grandparent/{grandparent}/parent/{parent}/item/{item}', ['uses' => 'AggregateController#handleShow', 'as' => 'grandparent.parent.item.show']);
app/Http/Controllers/AggregateController.php:
class AggregateController extends Controller
{
public function handleShow(Request $request, ...$items)
{
dd($request->path(), $items);
}
}
http://stackoverflow42005960.dev/grandparent/1/parent/2/item/3:
"grandparent/1/parent/2/item/3"
array:3 [▼
0 => "1"
1 => "2"
2 => "3"
]
If you still have issues with getting the variable arguments, then check your PHP version and if < 5.6 you'll have to use func_get_args()
There're many ways to go about this. For example, you can use a comma separated list in routes and simply explode in controller.
The way you have it currently, you will have to use a fixed number of optional parameters, e.g.
public function show(Request $request, $id1, $id2 = false, $id3 = false)
{
//if parent item exists
if($id2)
{
//if grandparent item resource
if($id3)
{
}
}
else
{
//just item
}
}
I have create custom router class in cakephp 2.x, I'm just follow this blog post. In my app i don't have /Routing/Route folders and I create folders and put StaticSlugRoute.php file to it. In that file include following code
<?php
App::uses('Event', 'Model');
App::uses('CakeRoute', 'Routing/Route');
App::uses('ClassRegistry', 'Utility');
class StaticSlugRoute extends CakeRoute {
public function parse($url) {
$params = parent::parse($url);
if (empty($params)) {
return false;
}
$this->Event = ClassRegistry::init('Event');
$title = $params['title'];
$event = $this->Event->find('first', array(
'conditions' => array(
'Event.title' => $title,
),
'fields' => array('Event.id'),
'recursive' => -1,
));
if ($event) {
$params['pass'] = array($event['Event']['id']);
return $params;
}
return false;
}
}
?>
I add this code but it didn't seems to working (event/index is working correct).I want to route 'www.example.com/events/event title' url to 'www.example.com/events/index/id'. Is there any thing i missing or i need to import this code to any where. If it is possible to redirect this type of ('www.example.com/event title') url.
Custom route classes should be inside /Lib/Routing/Route rather than /Routing/Route.
You'll then need to import your custom class inside your routes.php file.
App::uses('StaticSlugRoute', 'Lib/Routing/Route');
Router::connect('/events/:slug', array('controller' => 'events', 'action' => 'index'), array('routeClass' => 'StaticSlugRoute'));
This tells CakePhp to use your custom routing class for the URLs that look like /events/:slug (ex: /events/event-title).
Side Note: Don't forget to properly index the appropriate database field to avoid a serious performance hit when the number of rows increases.
Hi I'm trying to make an API to send data encapsulating with json.
as cakephp manual said, I added extensions in routes.php
$routes->extensions(['json]);
and I've made an index function in controller.
public function index(){
$item = $this->Items->find('all');
$this->set(['items' => $items, '_serialize' => ['items']]);
}
here are the problem.
what should i do after this to make api encapsulating with json??
Please help.
thank you
According to Cake 2.x Book (http://book.cakephp.org/2.0/en/development/rest.html)
You have to add this to your routes.php file:
Router::mapResources('items');
Router::parseExtensions();
Then, in your items controller, add the RequestHandler to your components array:
public $components = array('RequestHandler');
Then, in your items controller, add your methods, in your example:
public function index() {
$recipes = $this->Items->find('all');
$this->set(array(
'items' => $items,
'_serialize' => array('items')
));
}
Note: according to model names convention you should call $this->Item instead of $this->Items unless you previously defined the model name as "Item" (singular) in your item model file.
Finally, the API is done, you can access to yourprojecturl/items.json and see the json result.
I had trouble with RicardoCamacho's code until I used the $recipes, which is $this->Items->find('all'); in the $this->set(array...
public function index() {
$recipes = $this->Items->find('all');
$this->set(array(
'items' => $recipes,
'_serialize' => array('items')
));
}