I am trying to get my CakePHP app to use slugs instead of ids. I have read several tutorials and the CakePHP book about it, but I must be missing something simple.
My table has a "slug" field that I want to use for the URL instead of the default id.
I changed my ItemsController view to this:
public function view($slug = null) {
if (!$this->Item->exists($slug)) {
throw new NotFoundException(__('Invalid item'));
}
$this->set('item', $this->Item->findBySlug($slug));
}
And added this to my routes.php
Router::connect(
'/items/:slug',
array('controller' => 'items', 'action'=>'view'),
array('pass'=>array('slug'))
);
Yet I still get "Invalid Item, requested address not found..." when going to:
mycakeapp/items/slug-value
However, if I change everything from 'slug' to 'id' then the URL:
mycakeapp/items/id-value
works just fine
Can someone help me? Thanks in advance.
Well, read the documentation for Model::exists().
Returns true if a record with particular ID exists.
If $id is not passed it calls Model::getID() to obtain the current
record ID, and then performs a Model::find('count') on the currently
configured datasource to ascertain the existence of the record in
persistent storage.
It expects an id not a slug.
Here is a proper example from a model method to display an artist:
public function view($id = null, $options = array()) {
$defaults = array(
'contain' => array(
/* ... */
),
'conditions' => array(
'OR' => array(
$this->alias . '.' . $this->primaryKey => $id,
$this->alias . '.slug' => $id
)
)
);
$artist = $this->find('first', Hash::merge($defaults, $options));
if (empty($artist)) {
throw new NotFoundException(__('Invalid Artist'));
}
return $artist;
}
The controllers try/catches the exception and sets the exception message to the session by calling Session->setFlasH(). Easy. :)
Related
I'm trying to use the cache groups, but the examples in the documentation are not very clear for me.
This is my bootstrap:
Cache::config('default', array(
'engine' => $engine,
'duration' => $duration,
'prefix' => $prefix,
'groups' => array('page', 'photo', 'post')
));
We suppose that I have the model, including articles, pages, photos, etc., and in the various actions the data is written to the cache.
For example, PagesController:
public function index() {
$pages = Cache::read($cache = 'pages_index');
if(empty($pages)) {
$pages = $this->Page->find('all');
Cache::write($cache, $pages);
}
$this->set(array(
'pages' => $pages
));
}
This creates the file
tmp/cache/pages_index
The cache is working correctly, the next request will use the cache.
Others actions write the data for the Page model page.
Again PagesController:
public function view($slug = NULL) {
$page = Cache::read($cache = sprintf('pages_view_%s', $slug));
//If the data are not available from the cache
if(empty($page)) {
$page = $this->Page->find('first', array(
'conditions' => array('slug' => $slug)
));
if(empty($page))
throw new NotFoundException(__('Invalid page'));
Cache::write($cache, $page);
}
$this->set(array(
'page' => $page
));
}
This also works correctly.
Now I wish that editing a page being deleted from the cache all the data about pages. The same should happen for the other models (posts, photos, etc).
So, in my Page model:
public function afterSave($created, $options = array()) {
Cache::clearGroup('pages');
}
But this does not work: no files are deleted from the cache.
Where am I doing wrong? What I do not understand?
Thanks!
Because Your group name is 'page', not 'pages'.
I am working on cakephp now.Please explain the process of custom mysql query in cake php.
we have to write query by using
$this->Model->query();
and I return this to controller.In controller,i loaded the model in particular function and i called the function and set that function to view like this
$this->set('post',$this->User->get());
is it correct process?please explain the code ...
What query do you want to write this way? It is possible to write nearly all queries using the CakePHP ORM. Using the query building functions of CakePHP is the prefered way of doing it.
All data fetching an manipulation should be done in a model as well. See Separation of Concerns.
Here is a complete model method to fetch an user record based on its id OR slug.
public function view($userId, $options = []) {
$defaults = array(
'contain' => array(),
'conditions' => array(
'OR' => array(
$this->alias . '.' . $this->primaryKey => $userId,
$this->alias . '.slug' => $userId,
),
$this->alias . '.email_verified' => 1
),
);
$result = $this->find('first', Hash::merge($defaults, $options));
if (empty($result)) {
throw new NotFoundException(__d('user_tools', 'User not found!'));
}
return $result;
}
Controller:
public function view($userId = null) {
$this->set('user', $this->User->view($userId);
}
Alternative but NOT preferred mode method to fetch te data
public function view($userId, $options = []) {
$result = $this->query(/* Your query here */);
if (empty($result)) {
throw new NotFoundException(__d('user_tools', 'User not found!'));
}
return $result;
}
Your solution is correct, let me eleborate it in more detail
1st Solution
In your controller
$arrayTemp =array();
$arrayTemp = $this->ModelName->query(' Your Query String ');
$this->set('post',$arrayTemp);
2nd Solution
In your model class
function returnDate(){
return $this->query('Your Query String')
}
In Controller class
$arrayTemp = $this->ModelName->returnDate();
$this->set('post',$arrayTemp);
}
I read the documentation regarding file downloads, however I can't seem to get this to work.
I have read through questions here as well, and have had no luck.
My function looks as follows:
public function generate($id) {
$this->layout = 'ajax';
$this->Magazine->recursive = 2;
$DistributionLists = $this->Magazine->DistributionList->find('all',
array(
'conditions' => array(
'Magazine.id' => $id
),
'order' => array(
'DistributionList.priority ASC'
)
)
);
$this->set('magazine',$DistributionLists[0]['Magazine']['magazine_name']);
$this->set(compact('DistributionLists'));
}
public function download() {
$this->viewClass = 'Media';
$params = array(
'id' => "Magazine Distribution List.doc",
'name' => "Magazine Distribution List",
'download' => true,
'extension' => 'doc',
'path' => APP . "tmp" . DS
);
$this->set($params);
unlink(APP."tmp".DS);
$this->redirect(array('action'=>'index'));
}
public function afterFilter() {
parent::afterFilter();
if($this->action == 'generate') {
$this->redirect(array('action'=>'download'));
}
}
The reason I have an afterFilter function is because the word document that needs to be downloaded is created in the view file.
Does anyone know why this doesn't work?
You have to remove the call to the redirect method in your download method because it prevents your view from getting "rendered" due to the redirect.
I've written static pages component for my application, where admins can dynamically add/edit/remove static content pages. these are saved in the database.
(e.g. you can create a page called "about" and can visit it at myapplication/about)
This is my routing for these pages:
$page = new StaticPage();
$slugs = $page->find('list', array(
'fields' => array('slug'),
'recursive' => -1,
'order' => 'StaticPage.slug DESC',
));
Router::connect('/:slug',
array('controller' => 'static_pages', 'action' => 'display'),
array(
'pass' => array('slug'),
'slug' => implode($slugs, '|')
)
);
Now i have the problem, that when you create a page which slug matches an existing controller (e.g. users), it overwrites the Route to the UsersController.
so i need something like a blacklist or similar: i began to write a validation rule, where i want to check if that controller exists. for cake 1.3 there was a function "loadController" which return false, if the controller did not exist, but for cake 2.x there is no such an function. am i missing this somehow? does it have a new name or is in a utility library now?
Or are there better ways to solve this?
you should try this : http://www.cleverweb.nl/cakephp/list-all-controllers-in-cakephp-2/
and by getting the list of all controllers you can easily exclude the name of controllers
This is my validation method for now:
$route = Router::parse($check['slug']);
$controllerName = Inflector::camelize($route['controller'] . 'Controller');
$aCtrlClasses = App::objects('controller');
foreach ($aCtrlClasses as $controller) {
if ($controller != 'AppController') {
// Load the controller
App::import('Controller', str_replace('Controller', '', $controller));
// Load the ApplicationController (if there is one)
App::import('Controller', 'AppController');
$controllers[] = $controller;
}
}
if (in_array($controllerName, $controllers)) {
return false;
} else {
return true;
}
I have a Model called Posts with a field called slug. I want to create a routing function to basically do this: www.mysite.com/slug. So, I created a Slug class that is used in the routes.php file, which looks like so, and it doesn't work like I want:
class SlugRoute extends CakeRoute {
function match($url)
{
App::import('Model', 'Post');
$Post = new Post();
$Post->find('first', array('conditions' => array('Post.slug' == $url)));
$id = $Post['id'];
return $id;
}
}
Here's the line of code I have in my routes.php file:
Router::connect('/:slug', array('controller' => 'posts', 'action' => 'view'), array('routeClass' => 'SlugRoute'));
All I want to do is perform a search on my Posts to match the input slug in the URL with what I have in my database, then return the ID and automatically link to that ID.
What am I doing wrong? Thanks in advance!!
I found this example which does the exact same thing you're doing but uses the parse method instead of the match method. Have a look : http://mark-story.com/posts/view/using-custom-route-classes-in-cakephp
Try this
$Post = new Post();
$data = $Post->find('first', array('conditions' => array('Post.slug' => $url)));
$id = $data['Post']['id'];