Route to remove action from URL in CakePHP 3 not working - cakephp

In my routes.php file, I've put this into Router::scope('/', function ($routes) {
$routes->connect(
'/:controller/:id',
['action' => 'view'],
['id' => '[0-9]+']
);
However it does not seem to work? The URLs still go to players/view/1
I'm not sure what I'm doing wrong

From the book: book.cakephp.org/3.0/en/development/routing.html#route-elements (just below the code you may have cut-and-pasted):
CakePHP does not automatically produce lowercased and dashed URLs when using the :controller parameter. If you need this, the above example could be rewritten like so:
$routes->connect(
'/:controller/:id',
['action' => 'view'],
['id' => '[0-9]+', 'routeClass' => 'DashedRoute']
);
...and then a little further down:
Once this route has been defined, requesting /apples/5 would call the view() method of the ApplesController. Inside the view() method, you would need to access the passed ID at $this->request->params['id'].
So in my (for example) ApplesController's view() method, I added the following at the very top:
if ($id == null) {
$id = $this->request->params['id'];
}
...and now apples/view/5 and apples/5 both work the same way.

Related

How to use route elements instead of query string arguments for pagination in CakePHP 4.x?

My website's pagination urls worked like this, but stopped working when i upgraded to 4.x - for example: 1st page: mywebsite.com/new - 2nd page: mywebsite.com/new/page/2, etc. I did that with the following code:
//routes.php
$routes->connect('/new/page/:page',
['controller' => 'Articles', 'action' => 'latest'], [
'pass' => [
'page'
],
'page' => '\d+'
]);
$routes->connect('/new', ['controller' => 'Articles', 'action' => 'latest']);
then in my view/pagination-element I have the following:
//view
$this->Paginator->options(['url' => ['controller' => 'Articles', 'action' => 'latest']]);
The URLs mywebsite.com/new/page/2, 3, etc. are still working when accessing them directly, but links created through the paginator in the view are mywebsite.com/new?page=2, 3, etc. when they should be mywebsite.com/new/page/2, 3, etc.
can someone point me in the right direction?
I'd suggest that you consider possibly changing your URL schema to use the query argument style. Using route/URI elements just means more work to get the paginator features working properly. Also all the arguments (SEO, readability, etc) made for using "pretty URLs" for pagination, that were so popular back in the days, turned out to be nothing but hot air.
That being said, you'd have to use a custom/extended paginator helper to change how URLs are being generated, as by default the helper will explicitly set the page parameter (and all other pagination related parameters) as query string arguments. You have control over all generated URLs if you override \Cake\View\Helper\PaginatorHelper::generateUrlParams().
A quick and dirty example:
// src/View/Helper/PaginatorHelper.php
/*
Load in `AppView::initialize()` via:
$this->loadHelper('Paginator', [
'className' => \App\View\Helper\PaginatorHelper::class
]);
*/
declare(strict_types=1);
namespace App\View\Helper;
class PaginatorHelper extends \Cake\View\Helper\PaginatorHelper
{
public function generateUrlParams(array $options = [], ?string $model = null, array $url = []): array
{
$params = parent::generateUrlParams($options, $model, $url);
if (isset($params['?']['page'])) {
$params[0] = $params['?']['page'];
unset($params['?']['page']);
} else {
$params[0] = 1;
}
return $params;
}
}
That would move to the page parameter value in the URL array from the query string config to a regular URL parameter, ie turn
['controller' => 'Articles', 'action' => 'latest', '?' => ['page' => 1, /* ... */]]
into
['controller' => 'Articles', 'action' => 'latest', 1, '?' => [/* ... */]]
But again, I'd strongly suggest considering to switch to the query string URL schema.
See also
Cookbook > Views > Helpers > Configuring Helpers > Aliasing Helpers
Cookbook > Views > Helpers > Creating helpers

CakePHP 3 Add Action Routes to Index or Returns Null

I am trying to pass JSON into an Add() action of my controller but the response I get is either a response from the Index() action or "null" depending on entries in my routes.php (shown below).
I'm using Firefox plugin RESTClient to test/pass json to my actions:
Method: POST
URL: http://MyApp/notes.json (my understanding is this should call Add action based on cakephp docs )
JSON:
{
"post_id":"123",
"note":"hello world"
}
When I have the following in my routes.php, the response is "null"
Router::scope('/', function ($routes) {
$routes->extensions(['json']);
$routes->resources('Notes');
Removing $routes->resources('Notes'); from this scope returns a response from the index action and all items in the Notes model are returned in the response.
Furthermore, when I implement this through Crud.Add in an api prefix, I get valid results. That being said trying to understand what I have incorrect in my routes.php or add action that could be causing the null or routing to index action.
Add Action:
public function add()
{
$note = $this->Notes->newEntity();
if ($this->request->is('post')) {
$note = $this->Notes->patchEntity($note, $this->request->data);
if ($this->Notes->save($note)) {
$message = 'Saved';
} else {
$message = 'Error';
}
}
$this->set('_serialize', ['note', 'message']);
}
Routes.php:
<?php
use Cake\Core\Plugin;
use Cake\Routing\Router;
Router::defaultRouteClass('DashedRoute');
Router::extensions(['json'], ['xml']);
Router::prefix('api',function($routes) {
$routes->extensions(['json','xml']);
$routes->resources('Notes');
});
Router::scope('/', function ($routes) {
$routes->extensions(['json']);
$routes->resources('Notes');
$routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
$routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
$routes->fallbacks('DashedRoute');
});
Plugin::routes();
Thanks in advance.
You didn't set any variables for the view, other than serialization options, so you get null because there's nothing to serialize, ie you are missing something like
$this->set(compact('note', 'message'));
in your action.
And without the resource routes a request to /notes maps to the index action of the Notes controller, because that's how fallback routes are connected, no action = index action.
See also
Cookbook > Views > JSON And XML Views > Using Data Views with the Serialize Key
Cookbook > Routing > Fallbacks Method

Cakephp 3 redirect controller

In my CakePHP 3 application, after a controller redirect I get this error:
Error: [LogicException] Controller action can only return an instance of Response
Request URL: /mycontroller/
Stack Trace:
#0 /var/www/vhosts/example.com/httpdocs/vendor/cakephp/cakephp/src/Routing/Dispatcher.php(87): Cake\Routing\Dispatcher->_invoke(Object(App\Controller\SigninsController))
#1 /var/www/vhosts/example.com/httpdocs/webroot/index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#2 {main}
Regarding to CakePHP 2 controller documentation redirect was like this:
$this->redirect('/orders/thanks');
$this->redirect('http://www.example.com');
return $this->redirect(
array('controller' => 'orders', 'action' => 'confirm')
);
But in CakePHP 3 documentation it seems like this:
return $this->redirect('/orders/thanks');
return $this->redirect('http://www.example.com');
return $this->redirect(
['controller' => 'Orders', 'action' => 'thanks']
);
When I add return word before $this->redirect, error is solved. So does this makes problem ? Because I couldn't see a return redirect part in cakephp 3 migration guide. Migration guide only mentions that third parameter is dropped.
Return in CakePHP 3 application is used when you want to redirect to other pages or we can say actions/methods of other controllers or even if you want to redirect to 3rd Party Links like you have specified :-
return $this->redirect('/orders/thanks');
return $this->redirect('http://www.example.com');
return $this->redirect(['controller' => 'Orders', 'action' => 'thanks']);
And if you want to redirect to other method in the same controller then you can leave the return and use setAction as below but here URL will remain the same.
$this->setAction('thanks');
Return is better approach and URL will redirect if you use below code.
return $this->redirect(['action' => 'thanks']);
Take a look at this
Response provides an interface to wrap the common response-related tasks such as:
Sending headers for redirects.
and this
You should return the response instance from your action to prevent view rendering and let the dispatcher handle actual redirection.
if you are using route name then use this-
$this->redirect(['_name' => 'view-details']);
you are using route then use this-
$this->redirect('/page/view-details');
and last option is this
$this->redirect(['controller' => 'Pages', 'action' => 'view']);
----------------------------------------------------------------------
if you are use route name in view file then this use-
use Cake\Routing\Router;
echo Router::url(['_name' => 'view-details']);
echo Router::url(['controller' => 'Articles', 'action' => 'view', 'id' => 15]);
In cakephp, 3.6 to go to specific view/page Use $this->render('filename');
like below
public function search()
{
$this->render();
$this->render('profile');
}
You need to turn off Autorendering
since cakephp methods by default want to end with a redirect to a CTP file
use $this->autoRender = false
inside you function

Cakephp load element with BBcode/shortcode

I am looking for input/help on how to do this. Might be some PHP/cake developers could provide some good solutions here. Cakephp 2.3 something :)
Problem; How to put shortcodes in wysiwyg editor (example: [slideshow=1]slideshow here[/slideshow]) and render an element (in this case, loading and displaying the slideshow with ID=1).
ShortcodeHelper.php
App::import('Helper', 'Html', 'Router');
class ShortcodeHelper extends AppHelper {
public $shortcodes = array(
'slideshow' => '/(\[slideshow=)(.+?)(\])(.+?)(\[\/slideshow\])/'
);
public $returncodes = array(
//'slideshow' => $this->render('/elements/slideshow', array('id'=>'\\2'))
'slideshow' => '<strong rel="\\2">\\4</strong>'
);
public function render($content, $render=null) {
$shortcodes = $this->shortcodes;
$returncodes = $this->returncodes;
if(isset($render)) {
$temp_shortcodes = array();
$temp_returncodes = array();
foreach ($render as $key => $value) {
$temp_shortcodes[$key] = $shortcodes[$value];
$temp_returncodes[$key] = $returncodes[$value];
}
$returncodes = $temp_returncodes;
$shortcodes = $temp_shortcodes;
}
$return = preg_replace($shortcodes, $returncodes, $content);
return $this->output($return);
}
}
view.ctp (call render function from helper, and run the page-content trough it):
<?php echo $this->Shortcode->render($page['Page']['body']); ?>
Thanks. You are awesome!! :)
-Tom
You need to turn the short code string into a method call, parse it.
Your helper will need to be able to detect them and then break them up. Your code needs to be mapped somehow to a callback.
// [slideshow=1]slideshow here[/slideshow]
$this->requestAction(array('controller' => 'slideshows', 'action' => 'view', $id);
For example.
I think the best way here would be to just always map the first arg, the "function call" to an element instead and pass all other args to the element. This way you can do there whatever you want and request the data or just simply display HTML only.
I would put the mapping of short codes into something like Configure::write('ShortCodes', $shortCodeArray); this way plugins could even register their callback mapping by simply adding them to that array.
array(
'slideshow' => array('controller' => 'slideshows', 'action' => 'view')
);
You'll have to merge that with args from the parsed short code.
Why requestAction()? You should not violate the MVC pattern, for this reason you'll have to request the data via requestAction().

CakePHP - Using the RSS Helper without RequestHandler Component

I have set up the RSS Helper (with CakePHP 1.3.4) and all is working - I can access my feeds by /news/feed.rss - as exampled at http://book.cakephp.org/view/1461/Creating-an-RSS-feed-with-the-RssHelper
But I want to be able to do this conditionally, in sudo, something like:
if (!empty($var)) {
switch ($var) {
case one :
$xml = $this->method->find('$var conditions...');
... use RSS Helper to serve results as XML.
case two :
$xml = $this->method->find('other $var conditions...');
... use RSS Helper to serve results as XML.
}
}
Can I use the RSS Helper in this circumstance? What calls/syntax do I use?
There are 2 ways to do this.
Basically you can pass a variable like this:
http://yourserver.com/news/feed.rss?recent=20
and then in the controller you can access this variable with
$this->params['url']['recent']; //20
Or you can add a line in your Router file like this:
Router::connect('/feed-:recent/*', array('plugin'=>false, 'controller' => 'news', 'action' => 'feed'), array('recent'=>'[0-9]+'));
This way your url will look like:
http://yourserver.com/news/feed-20.rss
and finally I believe that url like this will work as well:
http://yourserver.com/news/feed.rss/recent:20
You can pass params to the function the same as any other controller call.
IE
public function rss( $limit = 20, $topic = null ){
if( !$topic ){
$xml = $this->Article->find( 'all', array( 'limit' => $limit ));
...
} else {
$xml = $this->Article->find( 'all', array( 'limit' => $limit, 'conditions' => array( 'Article.topic' => $topic )));
}
...
}
}
?>
Then you can access the rss feed with: http://domain/articles/rss.rss
If you need to pass parameters: http://domain/articles/rss/100.rss (fetch all with 100 results)
Or like this: http://domain/articles/rss/10/obama.rss (fetch all with the topic "obama" and return 10 items.
The RequestHandler allows you to use a url like /posts/index.rss and it automatically loads the RSS Helper and sends the output to /views/posts/rss/index.ctp, and uses layout at /views/layouts/rss/default.ctp.
But if you want to use the RSS Helper on it's own you need to:
1) include it the controller
var $helpers = array('Rss');
2) in your controller action you need to specify where the output goes, so
$this->render('/posts/rss/index','/rss/default');
In this case I also specified which layout to use in the 2nd argument. The first arg - the index.ctp file location is relative to your views/ directory. The second arg - the layout is relative to your views/layouts/ directory.
So I have a method in my posts_controller that I use to identify which feed is wanted (through a passed var) and subsequently finds the posts and sends them to the correct views and layouts:
function rss(){
if (!empty($this->params['pass'])){
$ops=array(
'conditions'=>'where feedname_id=' . $this->params['pass'][0],
'order' => 'Post.created DESC',
'limit' => 10
);
$this->set('posts', $this->Post->find('all',$ops));
$this->render('/posts/rss/index','/rss/default');
} else {
$this->redirect(array('controller'=>'feednames','action'=>'index'));
}
}
There might be better ways to code this - if so please let me know.

Resources