Cakephp routing controller alias - php

I'm trying to do the same as this site, stackoverflow, do with their URLs.
CakePHP works like this: website/controller/action/
I want to config routing to achieve this:
myWebSite.com/questions/(question_id)/(question)/
eg: myWebSite.com/questions/12874722/cakephp-routing-controller-alias /
I didnt figured it out how to do this bold part of URL.

In your Config/routes.php
Router::connect('/questions/*', array('controller' => 'questions', 'action' => 'view'));
In Controller/QuestionsController.php
view action get question id as
public function view() {
$question_id = isset($this->request->params['pass'][0]) ? $this->request->params['pass'][0] : "";
$question = isset($this->request->params['pass'][1]) ? $this->request->params['pass'][1] : "";
if( empty($question_id) ) {
throw new NotFoundException('Could not find that question');
}
// if $question is empty then get slug from database and redirect to /question_id/question
// Get question details and set
}

Related

Rewriting route depending of parameter value Yii

I have several rules in Yii that allows me to rewrite some routes, where every will be pass to the action as a get parameter.
'<department>' => 'products/index',
'<department>/<category>' => 'products/index',
I want to explicitly write a rule that depending of the parameter value will change the url to whatever I want
example, right now I have an URL like this
www.mysite.com/Books+%26+Pencils which was rewritten because of this rule '<department>' => 'products/index', which is ok
I want to change that URL to www.mysite.com/books-pencils , if anyone know how to write a rule that compares the value of the deparment attribute and then rewrites it to whatever I want.
THanks
You can use a custom class to handle you special requests.
I have used sth like this, to get my custom URLs out of a database:
'urlManager'=>array(
'rules'=>array(
array(
'class' => 'application.components.UrlRule',
),
),
),
Then you create your custo class similar to this:
<?php
Yii::import("CBaseRule");
class UrlRule extends CBaseUrlRule
{
public function createUrl($manager,$route,$params,$ampersand)
{
// check for my special case of URL route, if not found, then return the unchaged route
preg_match("/^(.+)\/(.+)$/", $route, $r);
if(!is_array($r) or !isset($r[1]) or !isset($r[2])) {
return $route;
}
// handle your own route request, and create your url
$url = 'my-own-url/some-thing';
// check for any params, which i also want to add
$urlParams = $manager->createPathInfo($params,"=","&");
$return = trim($url,'/');
$return.= $urlParams ? "?" . $urlParams : "";
return $return;
}
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
{
// handle my special url request
$controller = '....';
$action = '.....';
// return the controller/action that should be used
return lcfirst($controller)."/".$action;
}
}
I do not know if this was what you wanted, but at least in this class you can do everything you need with the URL requested.
If you would e.g. like to redirect a lot of similar URLs with a 301 Redirect to 1 URL, you could think of sth like this in the parseUrl function
// check my route and params, and if I need to redirect
$request->redirect('/your/new/url/?params=bla',true,'301');
First of all, if you want to change a URL, you should do a redirect (in this case 301). To implement this logic you can use custom URL rule class.
Url manager configuration:
'rules' => array(
// custom url rule class
array(
'class' => 'application.components.MyUrlRule',
),
)
MyUrlRule class:
class MyUrlRule extends CBaseUrlRule
{
public function createUrl($manager,$route,$params,$ampersand)
{
// Logic used to create url.
// If you do not create urls using Yii::app()->createUrl() in your app,
// you can leave it empty.
}
public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
{
// modify url
$pathInfoCleaned = strtolower(preg_replace('+%26+', '-', $pathInfo));
// redirect if needed
if ($pathInfo !== $pathInfoCleaned) {
$request->redirect($pathInfoCleaned, true, 301);
}
// parse params from url
$params = explode('/', $pathInfo);
if (isset($params[0])) {
$_GET['department'] = $params[0];
if (isset($params[1])) {
$_GET['category'] = $params[1];
}
}
return 'products/index';
}
}

Saving data on GET request in CakePHP

I am building a simple mechanism where a user can like a post by clicking on a link. I'm using GET rather than POST as I want to allow the method to fire via the URL.
That been said how do I save data using GET? As the request data doesn't exist in this scenario... My model looks like:
class Like extends AppModel
{
public $name = 'Like';
public $belongsTo = array('User','Post');
}
and the method for adding looks like:
public function add( $id )
{
$post = $this->Post->find('first', array(
'conditions' => array('Post.id'=>Tiny::reverseTiny($id))
));
if (!$post)
{
throw new NotFoundException('404');
}
if($post['Post']['user_id'] == $this->Auth->user('id'))
{
$this->Session->setFlash('You can\'t like your own post... That\'s just silly!');
}
if ($this->Like->create())
{
$liked = $this->Like->find('first', array(
'conditions' => array('Like.id'=>Tiny::reverseTiny($id), 'Like.user_id'=>$this->Auth->user('id') )
));
if(!$liked){
$this->Like->saveField('user_id', $this->Auth->user('id'));
$this->Like->saveField('post_id', $post['Post']['id']);
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
} else {
$this->Session->setFlash('You already like this post!');
}
else
{
$this->Session->setFlash('Server broke!');
}
}
Can anyone help?
<?php echo $this->Html->link('1', array('controller'=>'followers','action'=>'add','id'=>Tiny::toTiny($post['Post']['id'])),
array('title'=>'Follow','class'=>'follow')); ?>
This part all works fine. It's saving a new row in the DB on GET that I'm struggling with.
Hi you just need to make a link to your controller action and pass you variable in the url.
to be clear the link on the post to like is in your post view :
$this->Html->link('like this post', array('controller' => 'like', 'action' => 'add', $postId))
It should render a link like this :
www.yourWebSite/likes/add/1 to like the postId 1,
variables after your action (add) are interpreted as variable for your controller action
if your fonction add had been
public function add($postId, $wathever){
}
the url should look like www.yourWebSite/likes/add/1/blabla
where 1 is the first var for the add action and blabla the second one and so on.
this is the equivalent of a non rewriting url : ?postId=1&whatever=blabla
EDIT :
if(!$liked){
//simulate the post behaviour
$this->request->data['Like']['user_id'] = $this->Auth->user('id');
$this->request->data['Like']['post_id'] = $post['Post']['id'];
//save the data
if ($this->Like->save($this->request->data)) {
$this->Session->setFlash(__('Thanks for your support !'));
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
} else {
$this->Session->setFlash('Server broke!');
}
}
How about using save with id=0 instead of create?
<?php
$like = array(
"Like" => array
(
"id" => 0,
"user_id" => $this->Auth->user("id"),
"post_id" => $post['Post']['id']
)
);
$result = $this->Like->save($like);
if(!$result){$this->Session->setFlash('Server broke!');}
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
?>

How to use beforeAction in Yii or slug troubles

Yii-jedis!
I'm working on some old Yii-project and must to add to them some features. Yii is quite logical framework but it has some things I couldn't understand. Perhaps I haven't understand Yii-way yet. So I'll describe my problem step-by-step. For impatients - briefly question at the end.
Intro: I want to add human-readable URLs to my project.
Now URLs looks like: www.site.com/article/359
And I want them to look like this: www.site.com/article/how-to-make-pretty-urls
Very important: old articles must be available on old format URLs, and new - on new URLs.
Step 1: First, I've updated rewrite rules in config/main.php:
'<controller:\w+>/<id:\S+>' => '<controller>/view',
And I've added new texturl column to article table. So we will store here human-readable-part-of-url for new articles. Then I've updated one article with texturl for tests.
Step 2: Application show articles in actionView of ArticleController so I've added there this code for preproccessing ID parameter:
if (is_numeric($id)) {
// User try to get /article/359
$model = $this->loadModel($id); // Article::model()->findByPk($id);
if ($model->text_url !== null) {
// If article with ID=359 have text url -> redirect to /article/text-url
$this->redirect(array('view', 'id' => $model->text_url), true, 301);
}
} else {
// User try to get /article/text-url
$model = Article::model()->findByAttributes(array('text_url' => $id));
$id = ($model !== null) ? $model->id : null ;
}
And then begin legacy code:
$model = $this->loadModel($id); // Load article by numeric ID
// etc
It works perfectly! But...
Step 3: But we have many actions with ID parameter! What we have to do? Update all actions with that code? I think it's ugly. I've found CController::beforeAction method. Looks good! So I declare beforeAction and place ID preproccessing there:
protected function beforeAction($action) {
$actionToRun = $action->getId();
$id = Yii::app()->getRequest()->getQuery('id');
if (is_numeric($id)) {
$model = $this->loadModel($id);
if ($model->text_url !== null) {
$this->redirect(array('view', 'id' => $model->text_url), true, 301);
}
} else {
$model = Article::model()->findByAttributes(array('text_url' => $id));
$id = ($model !== null) ? $model->id : null ;
}
return parent::beforeAction($action->runWithParams(array('id' => $id)));
}
Yes, it works with both URL-formats, but it executes actionView TWICE and shows page two times! What can I do with this? I've totally confused. Have I choose a right way to solve my problem?
Briefly: Can I proceess ID (GET-parameter) before execute of any actions and then run requested action (once!) with modified only ID parameter?
Last line should be:
return parent::beforeAction($action);
Also to ask you i didnt get your step:3.
As you said you have many controller and you don't need to write code in each file, so you are using beforeAction:
But you have only text_url related to article for all controllers??
$model = Article::model()->findByAttributes(array('text_url' => $id));
===== updated answer ======
I have changed this function, check now.
If $id is not nummeric then we will find it's id using model and set $_GET['id'], so in further controller it will use that numberic id.
protected function beforeAction($action) {
$id = Yii::app()->getRequest()->getQuery('id');
if(!is_numeric($id)) // $id = how-to-make-pretty-urls
{
$model = Article::model()->findByAttributes(array('text_url' => $id));
$_GET['id'] = $model->id ;
}
return parent::beforeAction($action);
}
Sorry, I haven't read it all carefully but have you considered using this extension?

php, how to send a manual post within the zend framework?

im not sure if the question was framed correctly, but here is my situation:
i have two actions: indexAction and searchAction
a third action looks something like this:
public function customsearchAction()
{
$request = $this->getRequest();
if($request->isPost()){
$category = $request->getParam('select_category');
$searchString = $request->getParam('header_search_form');
if($category == 'index'){
$this->_redirector->gotoSimple('index', 'index', null,
array('term' => $searchString )
);
}
if($category == 'search'){
$this->_redirector->gotoSimple('search', 'index', null,
array('term' => $searchString )
);
}
}
}
this is fine and dandy, the only problem is that the redirect adds the term as a get string instead of a post like i need it.
any ideas?
Browser redirect will always add term to GET for next request to process. What you can do here is use ZF MVC internal redirect using 'forward' .
$this->_forward('search','index',null,array('term' => $searchString ));
Inside your searchAction
$searchString = $this->_getParam('term');

Replace ID from CakePHP URL

How can I replace the /posts/view/id with /posts/view/code ?
code is a field in the database that contains 10 random numbers generated using mt_rand() function.
In the PostsController I have this function for viewing posts:
public function view($id = null) {
$this->Post->id = $id;
$this->set('post', $this->Post->read());
}
Now I want to use code instead of id.
Thank you for your answers!
UPDATE:::
Someone from the CakePHP Q&A solved my problem.
I'll put the codes here so if someone needs the same solution, it can be found here.
in your routes.php add this code:
Router::connect('/posts/view/:code', array('controller' => 'news', 'action' => 'view'), array('pass' => array('code')));
in your controller change the view method like this:
public function view($code = null) {
$this->set('post', $this->Post->findByCode($code));
}
Thank you so much!
i used cakePHP
just few times, but i think you should see routing in cakePHP, you should change rewrite of url
I think that would be a URL like /posts/view/id/code. Then using the ID, you grab the code from the database:
public function view($id = null, $code = null) {
$this->Post->id = $id;
//..DO CAKEPHP QUERY TO GET CODE USING ID HERE. ASSIGN TO $code.
$this->Post->code = $code;
$this->set('post', $this->Post->read());
}

Categories