Trouble getting Cake PHP JsHelper to work - cakephp

I seem to be beaten at the very first hurdle on this one, I can't seem to get a basic "Hello world" going on in cake PHP.
in /app/Controller/MyController.php I have:
public $helpers = array('Js' => array('Jquery'), 'Html', 'Form');
public $components = array('RequestHandler');
in /app/View/Layouts/default.ctp I have:
echo $this->Html->script('//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js');
echo $this->fetch('script');
echo $this->Js->writeBuffer();
?></head>
in /app/View/My/index.ctp I have:
$this->Js->alert('HelloWorld');
but I get no alerts! If I try:
echo $this->Js->alert('HelloWorld');
it prints out to the browser: (double quotes instead of the typed single quotes!?)
alert("HelloWorld");
but not wrapped in < script > tags or even a $(document).ready(function(){});
Have I missed something out?

JsHelper is pretty useless to be honest. I don't even bother with it properly, I just include JavaScript in my site as I would normally, but using Cake's methods to keep it within the framework.
Example layout would have jQuery included in the <head> like this:
app/View/Layouts/default.ctp
<head>
<?php
// Include jQuery
echo $this->Html->script('jquery-1.8.2.min');
// Want to send some glabal values to your scripts?
$this->Js->set(array(
'TEST' => 'Hello World',
'ROOT' => $this->Html->url( '/', true)
));
echo $this->Js->writeBuffer(array('onDomReady' => false));
// Include any other scripts you've set
echo $this->fetch('script');
?>
</head>
Then in your view, you might want to include a specific script for that page:
app/View/Pages/test.ctp
<?php echo $this->Html->script('test.js'); ?>
And you just keep all your JavaScript as usual in an external script:
app/webroot/js/test.js
$(document).ready(function() {
/**
* Alert the value we set in our layout. All JS vars that have been
* set are available in your JavaScript via the window.app object.
*/
alert(window.app.TEST + ' sent from ' + window.app.ROOT);
});

The general consensus is don't bother using the JsHelper, and is likely being removed in Cake 3 in any case.
I would put echo $this->Js->writeBuffer(); before </body>
I am not sure on the exact workings but I think your JS is working with echo because it is being output in the middle of your view, so when the page loads, it alerts. (The JS will be in the middle of your HTML output) rather than in the <head> or before </body>
The writeBuffer is being called before the view file is processed; so your view js does not get added to the buffer. I could be incorrect however.

Set the write buffer at the end of each ctp file and it should work

Related

How to load/execute a view template from a string in CakePHP

I've developed a CakePHP plugin that allows the site administrator to define custom reports as a list of SQL queries that are executed and the results displayed by a .ctp template.
Now I need to allow the administrator to edit the template, stored in the DB together with the report.
Therefore I need to render a template that is inside a string and not in a .ctp file and I could not find anything in the core that helps.
I considered initially the approach to write the templates in .ctp files and load them from there, but I suspect this solution is rigged with flaws re: the location of the files and related permissions.
A better solution seems to override the View class and add a method to do this.
Can anyone suggest a better approach ?
P.S. Security is not a concern here, since the administrator is basically a developer without access to the code.
In CakePHP 2.0:
The View::render() method imports the template file using
include
The include statement includes and evaluates the specified file.
The evaluated template is immediately executed in whatever scope it was included. To duplicate this functionality, you would need to use
eval()
Caution: The eval() language construct is very dangerous because it
allows execution of arbitrary PHP code. Its use thus is discouraged.
If you have carefully verified that there is no other option than to
use this construct, pay special attention not to pass any user
provided data into it without properly validating it beforehand.
(This warning is speaking to you, specifically)
...if you wish to continue... Here is a basic example of how you might achieve this:
$name = 'world';
$template = 'Hello <?php echo $name ?>... <br />';
echo $template;
// Output: Hello ...
eval(' ?>' . $template . '<?php ');
// Output: Hello world...
Which is (almost) exactly the same as:
$name = 'world';
$template = 'Hello <?php echo $name ?>... <br />';
file_put_contents('path/to/template.php', $template);
include 'path/to/template.php';
Except people won't yell at you for using eval()
In your CakePHP application:
app/View/EvaluatorView.php
class EvaluatorView extends View
{
public function renderRaw($template, $data = [])
{
if (empty($data)) {
$data = $this->viewVars;
}
extract($data, EXTR_SKIP);
ob_start();
eval(' ?>' . $template . '<?php ');
$output = ob_get_clean();
return $output;
}
}
app/Controller/ReportsController.php
class ReportsController extends AppController
{
public function report()
{
$this->set('name', 'John Galt');
$this->set('template', 'Who is <?php echo $name; ?>?');
$this->viewClass = 'Evaluator';
}
}
app/View/Reports/report.ctp
// Content ...
$this->renderRaw($template);
Alternatively, you may want to check out existing templating engines like: Mustache, Twig, and Smarty.
Hmmm.. Maybe create a variable that will store the generated code and just 'echo' this variable in ctp file.
I had similar problem (cakephp 3)
Controller method:
public function preview($id = null) {
$this->loadModel('Templates');
$tempate = $this
->Templates
->findById($id)
->first();
if(is_null($template)) {
$this->Flash->error(__('Template not found'));
return $this->redirect($this->referer());
}
$html = $template->html_content;
$this->set(compact('html'));
}
And preview.ctp is just:
<?= $html

cannot add css versioning with cakePHP html helper

I wish to add a version number to my css file but I don't see a way to do it with the html helper.
I see one example of adding a timestamp through the bootstrap.php but I want to be able to control the version number myself.
I am using cakephp 2.3
style.min.css?v=1
<?php
echo $this->Html->css('style.min', array('v'=>'1'));
echo $this->fetch('css');
?>
or
<?php
echo $this->Html->css('style.min?v=1');
echo $this->fetch('css');
?>
Just add the .css
echo $this->Html->css('style.min.css?v=1');
Although it's standard practice to omit the .css, it's acceptable to include it, and will allow you to add additional parameters to the end of the URL.
In CakePHP 3x you can do this in config/app.php.
/**
* Apply timestamps with the last modified time to static assets (js, css, images).
* Will append a querystring parameter containing the time the file was modified.
* This is useful for busting browser caches.
*
* Set to true to apply timestamps when debug is true. Set to 'force' to always
* enable timestamping regardless of debug value.
*/
'Asset' => [
// 'timestamp' => true,
],

how to write code inside javascript

I need to make a DOM substitution, something like this:
$("#target").html('<?php echo $html?>');
where the $html variable could be a complex markup
$html = '<div>
<input type="text" name="test" />
</div>';
Of course I need some kind of escaping, or the javascript engine will break for a syntax problem at the first crlf or quote. In rails there's a simple function escape_javascript that makes it very easy. Is there anything similar in cakephp?
I think using
$("#target").html('<?php echo $this->element("element_path"); ?>');
makes more sense. But it depends on what is in your element_path.ctp file.
On the other hand, it's a bit weird to put replacement HTML in like this. Espacially if it's a lott, I would make an ajax call to load the HTML and have a Controller function return the contents of the element.
$("#target").html('Loading...').load('/myController/loadHtml/');
and the myController
function loadHtml(){
$this->layout = false;
}
and the view for the function app/View/my/load_html.ctp:
<?php echo $this->element("element_path"); ?>
you can do this by using requestAction
in your view file add the below code
$html = $this->requestAction('/tests/func_name');
echo $this->Html->scriptBlock('
$("#target").html('. $this->Js->value($html) .');
');
And in your TestsController.
public function func_name() {
$this->layout = 'layout_name'; // The layout you want here for design.
$this->render('/Elements/element_name'); // you can directly render the content of element by writing like this.
}

What is a "template" good for in CakePHPs E-Mailcomponent?

I'm a bit confused using the Cake's (2.3) Email-Class. It appears that we are able to define a "template", a "layout" and a "theme" whereas I only understand the usage of Layouts (located in /app/View/Layouts/Emails).
It seems that everything can be defined in the Layout, but the Template seems do be necessary (at least an empty file) but I don't understand the context because to me it seems that it does not matter what I put in there.
The concept of the theme is even more nebulous to me. Maybe someone can give me a hint here. I found a discussin in a mailing-list which was not really enlightning. The documentation does not reveal this too.
http://book.cakephp.org/2.0/en/core-utility-libraries/email.html
--
Edit: Fixed confusing typo.
Edit2: CakeEmail is used directly - not the component.
template is view (in terms of ordinary pages)
layout for emails is as layout for views (in terms of ordinary pages)
layout should contain some common elements like logo and etc
and you can push data to templates like push data to view from controller
Please, check following example:
from custom EmailComponent
public function restore_password($user_to_send_restore_link) {
$email = new CakeEmail('default');
$email->emailFormat('both');
$email->template('restore_password', 'emaillayout');
$email->to(array($user_to_send_restore_link['User']['email']));
$email->from(array(GENERAL_FROM_EMAIL => 'seqrd support team'));
$subject = 'Restore password link';
$email->subject($subject);
$email_data = array(
'hash' => $user_to_send_restore_link['User']['hash']);
$email->viewVars($email_data);
return $email->send();
}
app/View/Emails/html/restore_password.ctp
<p> Please, follow link <?php echo $this->Html->link('restore password link', Router::url(array('controller' => 'users', 'action' => 'restore_password_form', $hash), true)); ?> to restore password</p>
app/View/Layouts/Emails/html/emaillayout.ctp
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title><?php echo $title_for_layout;?></title>
</head>
<body>
<?php echo $this->fetch('content');?>
</body>
</html>
Theme it's next step of abstraction, there you can fast change whole styles of all emails, but not change code significantly.
Notes: viewVars method pass variables not only into template, but in email layout too.

Including JS in default View in Cake PHP

I can do this in any of my View file in Cake PHP:
<?php echo $this->Html->script('myjs.js', false); ?>
but if I do the same thing in my default view (default.ctp) then the JS files don't load i.e. they don't get echoed. I have tried moving includes above and below <?php echo $scripts_for_layout ?> in the default view but they still don't get printed:
<?php echo $scripts_for_layout ?>
<?php echo $this->Html->script('myjs.js'); ?>
<?php echo $this->Html->script('myjs.js'); ?>
<?php echo $scripts_for_layout ?>
Any ideas?
Thank you :).
with or without an extension - this is not the reason why the script do not get echoed,
as mensch mentioned before it makes no difference because of:
source of html helper cakephp 2
if (strpos($url, '?') === false && substr($url, -3) !== '.js') {
$url .= '.js';
}
the reason is:
you have already inserted this script inside your view,
cake checks if the script is already inserted on the page
options - 'once' - Whether or not the script should be checked for uniqueness. If true scripts will only be included once, use false to allow
the same script to be included more than once per request.
by default the value of once is set to TRUE
remove the script from your view first and then try it with or without '.js'
P.S.: why the petervaz answer has worked for you:
because this check:
if ($options['once'] && isset($this->__includedScripts[$url])) {
return null;
}
made before check for file extension
so isset(__includedScripts['myjs']) == false // because first key was __includedScripts['myjs.js']
and you've got your script included
I have a project with scripts added just before $scripts_for_layout and they are displayed fine. The only difference is that I'm not adding the extension .js to the call, like this:
default.ctp:
<head>
<?php echo $this->Html->charset(); ?>
<title>
<?php echo "Site > $title_for_layout"; ?>
</title>
<?php
echo $this->Html->script('jquery-1.6.4');
echo $this->Html->script('jquery-ui-1.8.16.custom.min');
echo $this->Html->script('mercado');
echo $scripts_for_layout;
?>
</head>
Another thing I'd DEFINITELY recommend is to check that loading your JS in a view is 100% OK with everything.
Do NOT use $this->Html->script if you are unsure about:
--- the dependency of scripts on each other
--- the dependency of some plugins on other scripts
--- how many different layouts and/or views you want to use in the future.
For example, some plugins use jQuery but they require jQuery to be loaded BEFORE the plugin itself.
So if you put jQuery in a view or layout, the plugin's JS loads before jQuery and plugin may not work. If you want to make sure some script loads first, you can try this:
in your app/View/Helper/AppHelper.php:
class AppHelper extends Helper {
public $helpers = array('Html', 'Session');
public function beforeRender($viewFile){
echo $this->Html->script('jquery', array('inline'=>false));
}
}
Therefore you don't have to include jQuery in multiple views and it loads first.
$scripts_for_layout in a layout is a placeholder for all the scripts included in views, so if you include it or don't won't have any effect if you load scripts directly in the default.ctp.
That being said, adding the extension or leaving it out of the $this->Html->script() call shouldn't make a difference. What could be the case is that the HtmlHelper isn't called correctly (it's called by default in Cake 2.0), but you should be receiving error messages. Is debug set to 2 in /app/Config/core.php?

Resources