Cakephp 2.0 + Elements + requestAction = empty result - cakephp

I'm trying to use Elements in Cakephp 2.0 without luck. I have a model named Post, a controller named Posts and various views. In the layout I would like to include (for every page/view) a box with the most recent news (say 2).
So I created the element dok-posts.ctp
<?php $posts = $this->requestAction('posts/recentnews'); ?>
<?php foreach($posts as $post): ?>
<div class="Post">
....
In my PostsController I added the function recentnews()
public function recentnews(){
$posts = $this->Post->find('all',array('order' => 'Post.created DESC','limit' => 2));
if ($this->request->is('requested')) {
return $posts;
} else {
$this->set('posts', $posts);
}
}
In my layout, default.ctp I call my element
<?php echo $this->element('dok-posts'); ?>
The problem is that I get this message
Invalid argument supplied for foreach() [APP\View\Elements\dok-posts.ctp, line 9]
Debugging in dok-posts.php, right after the $this->requestAction, gives me an empty line. It seems that the recentnews function is not returning anything (debugging in the function returns an array with the posts found). Can anyone please tell me what am I doing wrong?

Since you found out that the action is actually called,
$posts = $this->requestAction('posts/recentnews');
is working correctly. Here, for clarity and extended configuration options (for later changes to the code), I suggest you to use a Router array instead of an URL
$posts = $this -> requestAction(array(
'controller' => 'posts',
'action' => 'recentnews'
));
Now to your actual problem...
Since you say, it always goes into the else branch,
$this->request->is('requested')
might not work as expected. Try this (it works perfect for me):
if (!empty($this -> request -> params['requested'])) {
return $posts;
}

In your app controller create beforefilter and getNewsElement function.
public function beforeFilter() {
parent::beforeFilter();
$data = $this->getNewsElement();
$this->set('posts',$data);
}
function getNewsElement(){
# Put Post model in uses
$posts = $this->Post->find('all',array('order' => 'Post.created DESC','limit' => 2));
return $posts;
}
dok-posts.ctp
#Remove <?php $posts = $this->requestAction('posts/recentnews'); ?>
<?php foreach($posts as $post): ?>
<div class="Post">
....
In PostsController
public function recentnews(){
$posts = $this->getNewsElement();
$this->set('posts', $posts);
}
This will solve your problem!

Try
<?php $posts = $this->requestAction('/posts/recentnews'); ?>
(note the leading forward slash)

I have followed the example in the Cakephp's guide. From the moment it does not passes the if statement it seems that there is a problem with the control.
$this->request->is('requested')
So I removed the
is->('requested')
adding
->params['requested']
and it works.
Thank you all for your help (especially wnstnsmth for the solution).

Try this
<?php $this->requestAction('/controllerName/functionName');?>

Related

Edit associated model from view of another model (CakePHP 3)

I am still fairly new to CakePHP, though I like to think I have some basic understanding.
I have made a basic blog, based on an 'articles' table and bake all, piece of cake so far ;D. Now I've added a 'comments' table. 'articles' hasMany 'comments' and 'comments' belongsTo 'articles'. I again baked all for both tables and edited the 'view' action in ArticlesController.php and Articles/view.ctp to display all the comments of an article. No issues yet.
Now I'd like to be able to add a comment on the 'view' page of an article, much like you can comment on this forum. So I've added an Html->Form to view.ctp and copied some parts from the comment's add() to the article's view(). Article's view action:
public function view($id = null) {
$article = $this->Articles->get($id, [
'contain' => ['Comments']
]);
// Part from the add-action from Comments
$comment = $this->Comments->newEntity();
if ($this->request->is('post')) {
$comment = $this->Comments->patchEntity($comment, $this->request->data);
if ($this->Comments->save($comment)) {
$this->Flash->success(__('The comment has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The comment could not be saved. Please, try again.'));
}
}
// Set the selected article
$this->set('article', $article);
$this->set('_serialize', ['article']);
}
A part from Articles/view.ctp :
<?php foreach ($article->comments as $comment) : ?>
<h5><?= $comment->author ?></h5>
<p><?= $comment->body ?></p>
<?php endforeach; ?>
<b>Add comment</b>
<?= $this->Form->create($comment) ?>
<?php
echo $this->Form->input('comment.author');
echo $this->Form->input('comment.body');
?>
<?= $this->Form->button(__('Submit Comment')) ?>
<?= $this->Form->end() ?>
But this gives me a fatal error, :
Error: Call to a member function newEntity() on boolean File
C:\xampp\htdocs\blog_simple\src\Controller\ArticlesController.php
Line: 45
Any suggestions on how to accomplish what I'm looking for?
Error: Call to a member function newEntity() on boolean File
C:\xampp\htdocs\blog_simple\src\Controller\ArticlesController.php
Line: 45
Because you are in Articles Controller and you are trying Comments related functions (without Loading Model).
You have two option.
If you have correct relationship set up, then append Articles to calls like,
$comment = $this->Comments->newEntity();
to
$comment = $this->Articles->Comments->newEntity();
Similarly do for all the comments PatchEntity and Save function.
Add
$this->loadModel('Comments');
before calling Comments related functions. No need to append Articles like mentioned in previous point. Because, we are loading model.
Try which one you prefer. Good luck!

Error in writing a function in Cakephp

I'm working with cakephp for project development. I wrote a function to get data from the database and do a summation.I have created a table called user with height and weight in it. I want to retrieve that data,add them, and to return that. Here is the function I wrote.
public function calculate()
{
$Height=$this->set('user',$this->User->find('first'));
$Weight=$this->set('user1',$this->User->find('first'));
$sum=($Height+$Weight);
$this->set('SUM',$sum);
}
In the view I wrote the following code.
<div class="page-content">
<?php echo $user['User']['weight']; ?>
<?php echo $user1['User']['height']; ?>
<?php echo $SUM ?>
</div
Height and weight values are getting displayed. But the sum value is displayed as zero. Please help me to fix this.
I found a better way to do the above calculation. I used the following code in the controller.
public function calculate($id=null)
{
if (!$this->User->exists($id)) {
throw new NotFoundException(__('Invalid user'));
}
$this->set('user1',$this->User->find('first', array('fields' => ('height'),'conditions' => array('User.id' =>$id))));
$this->set('user',$this->User->find('first', array('fields' => ('weight'),'conditions' => array('User.id' =>$id))));
}
My view looks similar to the one mentioned above. This works fine with me...
First you need get the field from find. (You can use find condition to filter de user id for example).
$user = $this->User->find('first', array(
'conditions' => array('User.id' => $id),
'fields' => array('User.height', 'User.weight') // fields from your DB
));
So, you can do a pr($user) to see the data and know what use.
After do the sum and set to the view. Cakephp Set

CakePHP 2.4 Throwing main application exceptions from plugins? (interference with requestAction?)

I'm turning a well-working piece of my CakePHP (v 2.44) application into a plugin, and I'm getting the strangest behavior when throwing exceptions from within a controller in my plugin: the exception handler/renderer is starting to use my main site's layout in app/View/Layouts/mylayout.ctp, and then interrupts it with the default layout from app/View/Layouts/error.ctp. Here's an extract:
<div><ul><li class="jsdnavpopup blog-menu-categories">
<a href='/blog/categories'>All Categories</a><nav>
<!DOCTYPE html PUBLIC "{trimmed for space}">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CakePHP: the rapid development php framework:Error Page</title>
If you notice where it looks like a new header is sent right in the middle of Cake's composing the layout. Right at the point where my layout is interrupted I load am element, and the element uses requestAction (no caching in my dev environment). If I remove the element, then my layout is rendered even more until the next element using requestAction is encountered. These requestAction calls aren't requesting an action from the plugin that raised the exception.
I'm using all of the default error handlers from app/Config/core.php. The only thing that's different is specifying $this->layout in the error views.
Now if I recreate the original error layouts and views in the plugin's View folder, things work like I would expect. But for experimental purposes I put a renamed copy of my main site's layout there, and the same thing. Any time an element using requestAction is encountered the exception interrupts the layout.
Any ideas?
I'll reiterate that everything works perfectly when throwing exceptions from anywhere in the app that's not a plugin.
Here's how I do it (as requested in the comments on the OP's question)
Inside my layout, I put the following piece of code where I want the top menu to appear:
<?php echo $this->element('Menu.top_navigation'); ?>
This is quite simple, and allows the HTML designers in my team to put the menu wherever they want to.
Of course there is more happening under the hood. Here are the other files you'll need to make this work:
Plugins/Menu/View/Elements/top_navigation.ctp:
$thisMenu = ClassRegistry::init('Menu.Menu');
$menus = $thisMenu->display_menu_widget(array('position'=> 'top'));
echo $this->element('Menu.display_ul', array('menus' => $menus));
Note that this is just a wrapper -- you can make more of these for footer navigation, or even make an element that allows you to pass in the position as an argument.
Plugins/Menu/View/Elements/display_ul.ctp:
<?php
if(empty($depth)){
$depth = 0;
}
switch($depth){
case 0:
$classes = "dropdown";
break;
default:
$classes = "";
break;
}
?>
<ul class="<?php echo $classes ?> depth_<?php echo $depth ?>">
<?php foreach ($menus as $menu): ?>
<li>
<a href="<?php echo $menu['MenuItem']['url']; ?>">
<?php echo $menu['MenuItem']['title']; ?>
</a>
<?php
if(count($menu['children']) > 0){
echo $this->element(
'Menu.display_ul',
array(
'menus' => $menu['children'],
'depth' => $depth + 1
)
);
}
?>
</li>
<?php endforeach; ?>
</ul>
Plugins/Menu/Model/Menu.php:
/**
* display_menu_widget method
*
* #param array $options
* #return void
*/
public function display_menu_widget($options = array()) {
$defaults = array(
'position' => 'top'
);
$settings = array_merge($defaults, $options);
$this->recursive = 0;
$menuItems = array();
$conditions = array(
'Menu.position' => $settings['position']
);
$menuDetails = $this->find('first', array('recursive' => -1, 'conditions' => $conditions));
$menuPosition = $menuDetails[$this->alias]['position'];
$parentId = $menuDetails[$this->alias][$this->primaryKey];
$conditions = array(
$this->MenuItem->alias . '.menu_id' => $parentId
);
$this->MenuItem->recursive = 0;
$cacheName = 'default' . 'ModelMenuItem';
$cacheKey = $this->generateCacheName(array('type' => 'threaded', 'conditions' => $conditions));
$menuItems = Cache::read($cacheKey, $cacheName);
if(empty($menuItems) || PWFunctions::forceCacheFlush()){
$menuItems = $this->MenuItem->find('threaded', array('conditions' => $conditions));
Cache::write($cacheKey, $menuItems, $cacheName);
}
return $menuItems;
}
There's probably more in that menu function than you need, I'm using some aggressive caching routines to reduce database load, I'd suggest only using the bare bones of that function before worrying about caching.
This solution works well for me, and I think that it's the solution that violates the MVC concepts the least -- when you, as a designer, are in HTML mode and think "Hey I need to display the top menu" then it's just one line in your view, and you're not having to worry about modifying controllers or models.
I've used this pattern throughout my current CakePHP projects, and it seems to work quite well.

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().

How to use tinymce plugin?

I have tried many times to use this plugin and I failed.
I am following documentation, but it does not work for me.
I am posting the simple code here, to know what wrong I am doing.
1-I put this plugin in this folder app/plugins
2- I add TinyMce helper to articles_controller
<?php
class ArticlesController extends AppController {
// good practice to include the name variable
var $name = 'articles';
// load any helpers used in the views
var $helpers = array('Html', 'Form','TinyMce.TinyMce');
/**
* index()
* main index page of the formats page
* url: /formats/index
*/
function index(){
// get all formats from database where status = 1
$articles = $this->Article->find("all") ;
$this->set('articles', $articles);
}
function admin_add() {
// if the form data is not empty
if (!empty($this->data)) {
// initialise the format model
$this->Article->save($this->data);
// set a flash message
$this->Session->setFlash('The Format has been saved');
// redirect
$this->redirect(array('action'=>'index'));
} else {
// set a flash message
$this->Session->setFlash('The Format could not be saved. Please, try again.','default', array('class' => 'flash_bad'));
}
}
}
?>
3- in the view file articles/admin_add.ctp I added the editor
// i think the problem in this code
<?php $this->TinyMce->editor(array(
'theme' => 'advanced'
)); ?>
<div class="formats form">
<?php echo $form->create('Article');?>
<fieldset>
<legend>Add a article</legend>
<?php
// create the form inputs
echo $this->Form->input('title');
echo $this->Form->input('content'); ?>
</fieldset>
<?php echo $form->end('Add');?>
</div>
<ul class="actions">
<li><?php echo $html->link('List Articles', array('action'=>'index'));?></li>
</ul>
You need to put tinymce files into your js assets
Then you have to add into section of your layout.
Then you'll need to init tinymce according to example provided on tinymce website (ex: full tinymce layout) and configure it according to your requirements.
I'd personally would not rely on such cake plugins, when actions required to get things working are not many and they are simple enough.

Resources