CakePHP, Routing for optional controller using generic controller otherwise - php

I'd like to make an application in CakePHP which manages exercises and users results. Users and results are not important in this question.
I want to have a possibility to add an exercise with adding only a specific table and line to .ini config file. Application should route to a GenericExercisesController if specific one doesn't exists. Controller should load a GenericExerciseModel if specific doesn't exists. I'd managed with model loading and partially with routing with controller like this:
In route.php
foreach(Configure::read('exercisesTables') as $exerciseName){
if( App::import('Controller', Inflector::pluralize($exerciseName))){
Router::connect('/exercises/'.Inflector::pluralize($exerciseName).'/:action', array('controller' => Inflector::pluralize($exerciseName)));
}else{
Router::connect('/exercises/'.Inflector::pluralize($exerciseName).'/:action', array('controller' => 'GenericExercises', 'fakeModel' => $exerciseName));
}
}
So if I want to load an exercise Foo I should use address:
http://example.com/exercises/Foos/view
And this works fine, doesn't matter if specific controller exists.
Problem begins when I use reverse routing to generate links in views. If exercise Foo have specific controller this works correctly:
print $this->Html->url(array('controller' => Inflector::pluralize($exerciseName), 'action' => 'view'));
produces:
/exercises/Foos/view
But when exercise Bar doesn't have specific controller then the same code produces:
/Bars
This causes a problem, there is no Bars Controller.
Temporarily I'm generating those links manually, but I don't think that this is the best solution:
print $this->Html->url("/".Configure::read('exerciseRoutingPrefix')."/".Inflector::pluralize($exerciseName)."/view");
Maybe someone of you know a better solution. Thank you for reading my question.
UPDATE 1:
Those are routes in route.php defined before foreach in order as they're in file:
Router::connect('/', array('controller' => 'questions', 'action' => 'regulations'));
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
Router::connect('/help', array('controller' => 'pages', 'action' => 'faq'));

If I understood your question correctly, you could try making a "MyHtmlHelper" which does some magic before parsing your options to the HtmlHelper::link();
You can do it like so:
/app/View/Helpes/MyHtmlHelper.php
Create a helper which extends the "HtmlHelper" like so:
<?php
App::uses('HtmlHelper', 'View/Helper');
class MyHtmlHelper extends HtmlHelper {
public function link($title, $url = null, $options = array(), $confirmMessage = false) {
//do your magic here and change the $title, $url or $options accordingly.
return parent::link($title, $url, $options, $confirmMessage);
}
}
Now in your controller, you should include the Helper like so (aliasing) $helpers = array('Html' => array('className' => 'MyHtml')); instead of $helpers = array('Html');.
I guess you can fill in the blanks on the public function link yourself. If not, feel free to ask more help :)
More about helpers
Off course it is possible to do this with every other Helper or method you can think off. HtmlHelper::link() is just used as example.

Related

passing the variable in cakephp

I am trying to pass the id of the person whosoever logs-in to my site. I am using Auth for login in cakephp. So can anyone tell me how to pass the value during the login and retrieve it at the home page? I am new to cakephp.
This is what I have tried.
app controller.php
class AppController extends Controller {
//...
public $components = array(
'Session',
'Auth' => array(
'loginRedirect' => array('controller' => 'members', 'action' => 'home',$member['Member']['id']),
'logoutRedirect' => array('controller' => 'members', 'action' => 'index')
)
);
MembersController.php
public function home($id = null) {
$this->Member->id = $id;
}
home.ctp
<div align='center'><h2>Hi user welcome to home page </h2></div>
<?php
//I am just trying to print the id of the logged in person
echo $this->Form->value('User.id');
?>
<?php
echo $this->Html->link('LogOut',array('controller'=>'users','action'=>'logout'));
?>
echo $this->Form->value('User.id');
Is part of the FormHelper.
If you would like to have your form prefilled, make use of the conventions. Simply create an input field like this:
echo $this->Form->input('User.id');
If the $this->request->data contains this value ($this->request->data['User']['id']) it will automatically fill your form.
In order to retrieve values from the AuthComponent You can simply do something like $userName= $this->Auth->user('name'); (Just make sure the column 'name' exists in your 'users` database).
In order to pass variables to your view, you can simply use the method set which is defined in the Controller, which is extended by the AppController which is extend by FooBarsController.
If you want to set this variable to your view (as mentioned earlier) use set.
$this->set('userName', $userName);
In your view you can now do:
printf('Welcome %s', $userName);
Blog tutorial to get your started
AuthComponent
FormHelper
How Views work
How Controllers work
How Models work

Routing in CakePHP to vanity urls

I was wondering if there was an easy and best practices way to make routes in CakePHP (routes.php file) to map userIDs to a vanity url?
I have (terrible way to do this) the following test code in my routes page:
$users = array
(
1 => 'firstname-lastname',
2 => 'firstname2-lastname2'
);
//profiles
foreach($users as $k => $v)
{
// LESSONS (Profiles)
Router::connect('/:user', array('controller' => 'teachers', 'action' => 'contentProfile', $k),
array('user' => '(?i:'.$v.')'));
}
The above code routes my teachers controller with conProfile as the action from:
mydomain.com/teachers/contentProfile/1
to
mydomain.com/firstname-lastname
Can I connect to the db from the routing page? Is that not a good idea in terms of performance? Let me know what's the best way to do this.
You can create a custom route class that will look up passed urls in the database and translate them to the correct user id. Setting a long cache time should mitigate any performance impact of hitting the DB.
The book documentation is a little thin, however, but the basic structure is this:
class TeachersRoute extends CakeRoute {
/**
* Modify incoming parameters so that controller receives the correct data
*/
function parse($url) {
$params = parent::parse($url);
// Add / modify parameter information
// The teacher id should be sent as the first value in the $params['pass'] array
return $params;
// Or return false if lookup failed
}
/**
* Modify parameters so calls like HtmlHelper::url() output the correct value
*/
function match($url) {
// modify parameters
// add $url['slug'] if only id provided
return parent::match($url);
}
And then in your routes:
Router::connect(
'/:slug',
array(
'controller' => 'teachers',
'action' => 'contentProfile'
),
array(
'slug' => '[a-zA-Z0-9_-]+'
'routeClass' => 'TeachersRoute',
)
);

Append param to current URL with view helper in Zend

In my layout-script I wish to create a link, appending [?|&]lang=en to the current url, using the url view helper. So, I want this to happen:
http://localhost/user/edit/1 => http://localhost/user/edit/1?lang=en
http://localhost/index?page=2 => http://localhost/index?page=2&lang=en
I have tried the solution suggested Zend Framework: Append query strings to current page url, accessing the router directly, but that does work.
My layout-script contains:
English
If the default route is used, it will append /lang/en, but when another route is used, nothing is appended at all.
So, is there any way to do this with in Zend without me having to parse the url?
Edit
Sorry for my faulty explanation. No, I haven't made a custom router. I have just added other routes. My bad. One route that doesn't work is:
$Routes = array(
'user' => array(
'route' => 'admin/user/:mode/:id',
'defaults' => array('controller' => 'admin', 'action' => 'user', 'mode'=>'', 'id' => 0)
)
);
foreach( $Routes as $k=>$v ) {
$route = new Zend_Controller_Router_Route($v['route'], $v['defaults']);
$router->addRoute($k, $route);
}
Upd:
You must add wildcard to your route or define 'lang' parameter explicitly.
'admin/user/:mode/:id/*'
Additionally, according to your comment, you can do something like this:
class controllerplugin extends Zend_Controller_Plugin_Abstract
{
function routeShutdown($request)
{
if($request->getParam('lang', false) {
//store lang
Zend_Controller_Front::getInstance()->getRouter()->setGlobalParam('lang', NULL); //check if this will remove lang from wildcard parameters, have no working zf installation here to check.
}
}
}

CakePHP Routing help

I want to make my /pages/about become just `/about
I tried doing it with the routing in routes.php but couldn't get it working e.g. Router::connect('/pages/about', array('controller' => 'pages', 'action' => 'display'));
can anyone help?
ALSO
for my portfolio controller I have it currently showing work like /portfolio/view/102 but I would like to display it something like /portfolio/view/Paperview_Magazine-102 where Paperview Magazine is the title of the post and 102 is the ID of the post. I have looked at the Cake Book but if someone could post up some code that'd be awesome.
Thanks
To make a route for /about, you need to make a route for /about, not /pages/about:
Router::connect('/about',
array('controller' => 'pages', 'action' => 'display', 'about'));
To use URLs like /portfolio/view/Paperview_Magazine-102, you can use standard routes, but you'll have to do a little work in the controller:
// PortfolioController
// $identifier == "Paperview_Magazine-102"
public function view($identifier) {
if (!preg_match('/^(.+)-(\d+)$/', $identifier, $matches)) {
// $identifier is not in format 'Title-Id'
$this->cakeError('error404');
}
// $matches[1] == Paperview_Magazine
// $matches[2] == 102
$post = $this->Portfolio->read(null, $matches[2]);
$this->set(compact('post'));
}
Maybe a bit of a long shot, but in the first one you could try Router::connect('/:controller/about', array('action'=>'display'));? I seem to remember having some similar problems with my routes and this maybe helping, though I can't remember why.
With the second question, can't you just rework the view function so it accepts a $slug parameter instead of $id, and find the right portfolio record by searching on the slug instead? Or check if the parameter is_numeric, and search for an id if it is, or a slug if it isn't...

Zend Framework Router dynamic routes

I bumped into a problem and I can't seem to find a good solution to make it work. I have to make some dynamic routes into a Zend Framework project. I'll explain shortly what my problem is:
I need to have dynamic custom routes that "extend" the default route (module/controller/action/params). The project I'm working for has several partners and the routes have to work with those.
To store the partners I've made a static class and it looks like this.
<?php
class App_Partner
{
static public $partners = array(
array(
'name' => 'partner1',
'picture' => 'partner1.jpg'
),
array(
'name' => 'partner2',
'picture' => 'partner2.jpg'
),
array(
'name' => 'partner3',
'picture' => 'partner3.jpg'
)
);
static public function routePartners() {
$partners = array();
foreach(self::$partners as $partner) {
array_push($partners, strtolower($partner['name']));
}
$regex = '(' . implode('|', $partners) . ')';
return $regex;
}
}
So App_Partner::routePartners() return me a string like (partner1|partner2|partner3) which I use to create the right routes. My goal is to have the custom routes for each partner for every route I have set in the Bootstrap. So if I have a route add-product.html set I want it to work for each partner as partner1/add-product.html, partner2/add-product.html and partner3/add-product.html.
Also, partner1/, partner2/, partner3 should route to default/index/index.
In fact, I made this thing to work using routes like the one below.
<?php
$routeProposal = new Zend_Controller_Router_Route_Regex(
App_Partner::routePartners() . '?/?proposals.html',
array(
'module' => 'default',
'controller' => 'proposal',
'action' => 'index',
'page' => 1
),
array( 1 => 'partner'),
"%s/proposals.html"
);
$router->addRoute('proposal', $routeProposal);
The problem
The above route works fine if I use a partner in the request URI, but if I don't, I get double slashes like public//proposals.html because of the reverse route set in the route above to be "%s/proposals.html". I can't seem to find a way to avoid this reverse route because I build my urls using the url view helper and if the reverse route isn't set I get an exception stating this.
I also need the routes to work without a partner set, which will be the default way (add-product.html, proposals.html etc).
From your description, it seems like you're looking for a zend router chain, where your partner is an optional chain.
Here's a similar question, but using a hostname route : Zend Framework: get subdomain parameter from route. I adapted it to solve your problem, just put the following in your Bootstrap.php to initialize the routing :
protected function _initRoute()
{
$this->bootstrap('FrontController');
$router = $this->getResource('FrontController')->getRouter();
// Default route
$router->removeDefaultRoutes();
$defaultRoute = new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index',
)
);
$router->addRoute('default', $defaultRoute);
$partnerRoute = new Zend_Controller_Router_Route(
':partner',
array('partner' => 'none'),
array('partner' => '^(partner1|partner2|partner3)$')
);
$router->addRoute('partner', $partnerRoute->chain($defaultRoute));
}
Change as you see fit. In your controllers you will only get a value for the partner parameter if it was actually specified AND valid (you will get a routing error if the partner doesn't exist)...
I use a similar process to detech lang, in my route (but with a ini file).
You can use a default value for you partners parameter to make the route working without partner, and add a ? to your regex.
But actually, I don't know how to avoid the double //...
Hope that helps.
EDIT: For your information, here is a simplified version of my route with language:
routes.lang.type = "Zend_Controller_Router_Route"
routes.lang.route = "lang/:language/*"
routes.lang.reqs.language = "^(en|fr|nl|de)?$"
routes.lang.defaults.language = none
routes.lang.defaults.module = default
routes.lang.defaults.controller = index
routes.lang.defaults.action = language

Categories