I previously use this code to add a custom ( not crud ) controller:view handled by backpack.
By handled I mean just let backpack do the templating and authentification job call my controller and serve my view in the subtemplate.
Route::group(['middleware' => ['web', 'auth'], 'prefix' => config('backpack.base.route_prefix', 'admin')], function () {
Route::get("/stats", 'MyStateController#index');
Route::get("/infos", 'MyInfosController#index');
});
It doesn't seems to work anymore as I get this error :
Symfony\Component\Routing\Exception\RouteNotFoundException Route
[login] not defined.
Same error if I remove the 'auth' middleware.
I dont find any documentation to this simple need. What should I do ?
Working code :
Route::group(['middleware' => ['web', 'admin'], 'prefix' => config('backpack.base.route_prefix', 'admin')], function () {
Route::get("/stats", 'MyStateController#index');
Route::get("/infos", 'MyInfosController#index');
});
Related
I've got very big trouble with custom operations in Laravel backpack.
The documentated setup is clear but lack a real exemple with a form.
In my case I wanted to use the form engine to create a form for a relationship.
First step I did this :
public function getProtocoleForm($id)
{
// Config base
$this->crud->hasAccessOrFail('update');
$this->crud->setOperation('protocole');
//
$this->crud->addFields([
[ 'name' => 'codeCim',
'type' => 'text',
'label' => 'Code CIM',
],
]);
$this->crud->addSaveAction([
'name' => 'save_action_protocole',
'visible' => function($crud) {
return true;
},
'button_text' => 'Ajouter le procotole',
'redirect' => function($crud, $request, $itemId) {
return $crud->route;
},
]);
// get the info for that entry
$this->data['entry'] = $this->crud->getEntry($id);
$this->data['crud'] = $this->crud;
$this->data['saveAction'] = $this->crud->getSaveAction();
$this->data['title'] = 'Protocole ' . $this->crud->entity_name;
return view('vendor.backpack.crud.protocoleform', $this->data);
}
This is working fine, the form appears on the screen, then I did a setup for a post route like this :
Route::post($segment . '/{id}/protocolestore', [
'as' => $routeName . '.protocolestore',
'uses' => $controller . '#storeProtocole',
'operation' => 'protocole',
]);
The route appears correctly when I execute the artisan command but the storeProtocole function is never called. I checked the generated HTML and the form action is correct and checking in the "network" panel of the browser is also targeting the correct route.
Can you help me and tell me where I missed something ?
[Quick update] I made a mistake, the form route is not good in the HTML, it takes the route of the main controller.
After tackling this other question we would now like to check if the authenticated user can view, update or delete an existing record. Since checkAccess() is called by default in all restActions the following seemed the most logic thing to try:
public function checkAccess($action, $model = null, $params = []) {
if(in_array($action, ['view', 'update', 'delete'])) {
if(Yii::$app->user->identity->customer->id === null
|| $model->customer_id !== Yii::$app->user->identity->customer->id) {
throw new \yii\web\ForbiddenHttpException('You can\'t '.$action.' this item.');
}
}
}
But the API seems to ignore this function. We added this function in our controller. The actions (view, update and delete) are the default restActions.
Our BaseController sets actions like this:
...
'view' => [
'class' => 'api\common\components\actions\ViewAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
'scenario' => $this->viewScenario,
],
...
Are we forgetting something?
Just add the following inside your custom action before executing any other code as it was done in the default view action (see source code here):
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id, $model);
}
note: $this->checkAccess is defined in parent yii\rest\Action so your custom ActionView class need to either extend yii\rest\Action or redefine the variable public $checkAccess;
We obviously should have seen that the viewAction is not the default but an altered api\common\components\actions\ViewAction ... Not sure how we missed that...
My login form may be called with a re-direct query and I am wondering if there is a simple way to include this in the subsequent post action.
The use case is for SSO login.
My normal login route is:
/customer/login
and when called from a third party client becomes:
/customer/login?redirectTo=http://www.example.com
My login action:
public function loginAction()
{
$prg = $this->prg();
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return new ViewModel(['form' => $this->loginForm]);
}
This loads my view and I currently define my action as so:
$form = $this->form;
$form->setAttribute('action', $this->url());
Now when the action is called, I am losing the redirectTo parameter.
So my question is this, is it possible to update the action to include the re-direct url so that when a user clicks to login, it is posted back to my form?
thanks!
EDIT -
Obviously I can create a redirectTo route in the configs and test on the initial call to the page for the existence of such a route and include this in the form. My question however is whether or not this can be done automagically simply from the viewscript.
To generate query string arguments from the view helper, you need to assign them as the third argument using the query key. Please refer to the ZF2 docs http://framework.zend.com/manual/current/en/modules/zend.view.helpers.url.html
$form->setAttribute('action', $this->url('application', array('action' => 'login'), array('query' => array('redirectTo' => 'http://www.example.com,))));
$form->setAttribute('action', $this->url('login', [
'query' => [
'redirectTo' => $this->params()->fromQuery('redirectTo')
]
]);
Where 'login' is the name of the login route.
See Url View Helper
Well my solution is not as elegant as I hoped it would be. I wanted to avoid using the controller for the query params. As #Stanimir pointed out, the view helpers are in fact, to help with view so my original idea was unfounded.
This is an end to end of what I have put together:
Controller:
$redirect_url = $this->params()->fromQuery('redirectTo',null);
Returns this to view on initial load:
return new ViewModel( ['form' => $this->loginForm , 'redirect_url' => $redirect_url] );
View
$form->setAttribute(
'action',
$this->url(
'customer/login', [] ,
[ 'query'=>
[ 'redirectTo' => $this->redirect_url ]
]
)
);
I have some trouble with the router.
I have a custom route :
$router->addRoute('showTopic',
new Zend_Controller_Router_Route('/forum/topic/:topic',
array('module' => 'forum',
'controller' => 'topic',
'action' => 'show'),
array('topic' => '\d+')));
But when I try to access this url : localhost/forum/topic/16
I get this error :
Fatal error: Uncaught exception 'Zend_Controller_Router_Exception' with message 'topic is not specified'
But I don't want to put a default value for topic, because I also want the route /forum/topic to list all topics...
Secondly, I know that if I add a custom route, the default router is overridden, but I need to have some default routes too. The only way I have found is to set 'default' in the second parameter of the url view helper, like this
$this->url(array(
'module' => 'forum',
'controller' => 'topic',
'action' => 'add'
), 'default', true)
Is there a more elegant way instead of doing this for all url where I want to use the default behavior ?
You should have a default value for a topic and add the more general route (the one for forum/topic) after the more specific one. Route_Rewrite checks the routes beginning with the last one (it actually does an array_inverse).
The url helper delegates assembly urls to a route, its second paremeter being the name of the route to pull from the router. Since the default route is registered under the name of 'default', there is nothing really inelegant in using the name (it is not a magic string or a special case). If this really bugs you, you could write a custom helper (to be placed under "views/helpers"):
class Zend_View_Helper_DefaultUrl extends Zend_View_Helper_Abstract {
public function defaultUrl($params) {
return $this->view->url($params, 'default');
}
}
And use it in your view like defaultUrl(array('action'=>'test')) ?>.
I use Navigation component for site menus. I also use let zend figure-out the selected menu item from request parameters - I guess this is done automatically. The only problem is, that for this to work, action and controller have to be specified in navigation configuration for every node. This also means that when zend generates links from route, action and controller information to appended to the generated link automatically.
Anyone had the same problem?
Zend manual section, explaining the Mvc navigation page features.
Example:
some route defined in bootstrap:
$router->addRoute('user_profile_tab', new Zend_Controller_Router_Route(
'profil/:user/:location/:tab/*',
array(
'action' => 'profile',
'controller' => 'user',
'user' => ($user ? $user->id : 0), //change later
'location' => 0 //inject appropriate value later
)
));
navigation container object:
$container = .....
......,
array(
'label' => tr('Privileges'),
'id' => 'user-profile-perms',
'type' => 'Zulu_Navigation_Page',
'controller' => 'user',
'action' => 'profile',
'route'=> 'user_profile_tab',
'params' => array('tab'=>Main_Lib_Common::NAVI_USER_TAB_PERMS)
)
);
the result when using
$page = $container->getById('user-profile-perms');
$page->href;
http://www.example.com/profil/1/0/3/controller/user/action/profile
WHY action and controler params in the navigation container object you ask. The $page->isActive() check needs this data to make a perfect match.
THE FIX:
extend mvc navigation page and provide an alternative getHref() method ... one that removes action, controller and module params when a route does not define them.
I have done this to fix this weird behaviour:
extend mvc navigation page
provide an alternative getHref() method
check for routes, not having action , controller and module parameters and remove them from params array before href generation.
This way the isActive matching will still work, as we didnt modify the route or navigation nodes in any way.