I am creating a basic routing system for a bespoke CMS and I am wanting to do this completely in raw PHP without frameworks. So far, I have the routing down, with GET and POST methods working correctly.
Here is my index.php file
<?php
include_once 'Request.php';
include_once 'Router.php';
$router = new Router(new Request);
$router->get('/', function() {
return <<<HTML
<h1>Hello world</h1>
HTML;
});
$router->get('/profile', function($request) {
return <<<HTML
<h1>Profile</h1>
HTML;
});
$router->get('/data', function($request) {
return json_encode($request->getBody());
});
As you can see, for the routes '/' and '/profile' I am returning some HTML code using heredoc. This is okay but will eventually fill up this file really quickly.
Is there a way to render a template (from inside a subfolder for example) in the space of the 'return html code'?
I assume your second parameter for the get method is a callback function that will be triggered in the function if the route matches? If that's the case I don't see why you return the HTML as it isn't used anywhere. You might want to echo() or print() instead.
If you want to return the code and output it later, you need to save the return value into a variable.
No matter which path you choose, you could use another include() and just put the relevant code into a separate file for each path if you want to make your main script more readable.
I'd also recommend to use require_once instead of include_once in the top as there's no point in continuing execution if these class files are missing.
Related
In Ruby on Rails In my controller I can do something like this:
def jstest
respond_to do |format|
format.js
end
end
This would return jstest.js.erb from the relevant views directory
jstest.js.erb
alert("Hello World")
The result being I get hello world alert.
I want to do the same thing in Laravel
In my controller I have:
public function jstest( )
{
return view('jstest');
}
in my view (which I have tried with both jstest.blade.js and just jstest.js)
but I get the error, view cannot be found.
The only way I get this to work is by calling the view jstest.blade.php and including my js in a <script> tag within this php file. but this feels a bit wrong...?
Is this even possible in Laravel? If so, where am I going wrong?
Example use case:
Imagine the following example, I have a table of comments, a user can click a delete button which will send an ajax request to delete the comment.
My Route:
Route::delete('post/comments/{comment}','commentsController#delete');
In my controller:
Public Function delete($comment)
{
$comment->delete();
return commentDeleted.js
}
commentDeleted.js:
$(#comment).remove();
So from the users perspective, they click delete and the comment disappears from their screen without loading a new page.
I don't know if Laravel supports this out of the box, but this is my take on your issue.
This is how I understood your problem: you want to specify a path to .js file as an argument to a function and you want this .js file to be immediately executed when loaded via browser.
I'd do it in these 2 steps
Create a jsview() function that accepts a path, similar to view()
Create the base .blade.php file which will be used by the above
The .blade.php contents
Assumption is that this view will be named javascript.blade.php and it's in resources/views/ directory.
<!DOCTYPE html>
<html lang="en">
<head>
<script type="text/javascript" src="{{$path}}"></script>
</head>
<body></body>
</html>
The function
function jsview($path)
{
return view('javascript', ['path' => $path]);
}
Usage
<?php
Route::get('/test', function()
{
return jsview('/assets/my.js');
});
This answer isn't really fully complete because your browser will be loading files from public/ directory so all .js files you specify this way would be read from there. I believe Laravel does have something related to resources, but I'm not expert enough on that matter.
This is posted only to outline how to achieve something that you need, but isn't available out of the box.
I am trying to roll my own MVC purely for learning purposes, but am hitting a snag when I try to load in the view files. Here's my problem:
Front controller grabs appropriate page controller, page controller include_once() the appropriate view. The view is a file containing HTML and PHP. When I return the include file from the controller, the included file content appear out of sequence of the PAGE layout intended. (example: I want to display HEADER CONTENT FOOTER, but when I include the view's contents, my controller spits out CONTENT, HEADER, 1, FOOTER).
In my front controller I am attempting to set my included file contents to a variable I then echo in between my HEAD and FOOTER in the PAGE template. But this does not work as intended. I can wrap my view file contents in: return "code" but I don't want the added headache of worrying about stray quotes/double quotes in my view code that could break the script. I have tried file_get_contents(), but this only spits out a string (albeit in the right place/sequence in my template) and does not the PHP in my view files correctly (ie at all).
My question is: Is there a way I can return my view file to my front controller using include_once() in a variable? I can't seem to find a adequate answer to this. I'd be happy to add my code if it would help.
You can assign a return value from an include(). A lesser-known feature of this function is that you can have a file like this:
<?php
// config.php
return array('database_host' => 'localhost');
and you can read it in thus:
<?php
$config = include('config.php');
However, you're wanting to do this:
<!-- text.html -->
<h1>Heading</h1>
<p>Hello</p>
and read it in with this:
$html = include('text.html);
You can try doing that, but as soon as the include() scans the file, the output will be sent to the web server. That's how PHP works. Since there is no explicit return value, moreover, the $html variable comes back empty.
Thus, we use the output buffering functions to modify PHP's behaviour: we start buffering, and then we fetch it and clear it. This is then not sent to Apache, unless of course you choose to turn off output buffering and echo what you have collected, in which case it goes back into the normal output buffer and is actually sent.
I'm trying to remove unwanted scripts from my custom joomla template header, which I've managed to remove everything except for this:
<script type="text/javascript">
jQuery(document).ready(function()
{
jQuery('.hasTooltip').tooltip({});
});
</script>
I've searched for hours and I've tried numerous things to get it removed, but I can't seem to get rid of it. Here's what I've done to remove the other scripts I don't want (for anyone else who has this issue):
# unset frameworks
JHtml::_('bootstrap.framework',false);
JHtml::_('jquery.framework',false);
# unset scripts
unset($doc->_scripts[$this->baseurl.'/media/jui/js/jquery.min.js']);
unset($doc->_scripts[$this->baseurl.'/media/jui/js/jquery-noconflict.js']);
unset($doc->_scripts[$this->baseurl.'/media/jui/js/bootstrap.min.js']);
If someone could help me remove that tooltip javascript, that would be fantastic. Oh and I don't want to touch the core files, so I'm trying to remove it from the template index.php file itself.
Some component/Module/Plugin is calling the function JHTML::_('behavior.tooltip'). and that function add your code.
you can do:
The dirty way: go to libraries\cms\html\bootstrap.php and change the file at the method tooltip(). but remember it's a core file so upgrade can overide your changes.
The clean way, find the component that adds this code and remove or change it.
Good Luck
You'll have to manually parse $doc->_script. The $doc->_scripts array contains scripts that are linked to another source while $doc->_script is for script declarations like the tooltip one.
I managed to get rid of the embedded javascript using this code in my template's index.php file:
unset($this->_script['text/javascript']);
disclaimer: I am not a PHP developer, so use above code at your own risk :)
At the end of your web root index.php (not template, http://domain.com/index.php) replace the $app->execute(); line with the following:
// Start the output buffer.
ob_start();
// Execute the application.
$app->execute();
// Get buffer
$buffer = ob_get_clean();
// Change HTML
$buffer = str_replace('<script type="text/javascript">'.chr(10).'jQuery(document).ready(function(){'.chr(10).' jQuery(\'.hasTooltip\').tooltip({"html": true,"container": "body"});'.chr(10).'});'.chr(10).' </script>','',$buffer);
// Output buffer
echo($buffer);
Note that you would need to use the EXACT HTML that's being created by the module. This is what my module is creating, yours could be slightly different.
In addition to doing HTML rewrites like this, you could also tidy the output, remove links to modules, etc.
I also use this for doing things like changing the copyright year on a website. In a module position, I reference the current year as "{year}" and then I add another str_replace which does the following:
$buffer = str_replace('{year}',date('Y'),$buffer);
Bingo, always shows the current year.
Enjoy...
Based on Joomla documents, 2 things to consider:
a) In the below code, you're actually enabling/including bootstrap and jquery:
# unset frameworks
JHtml::_('bootstrap.framework',false);// including bootstrap!
JHtml::_('jquery.framework',false);// including jquery!
When bootstrap is enabled, joomla automatically enables jquery, and if joomla enabled jquery, joomla automatically enables tooltip.
Just don't call these functions. The unset you used will remove bootstrap and jquery correctly:
unset($doc->_scripts[$this->baseurl.'/media/jui/js/jquery.min.js']);
unset($doc->_scripts[$this->baseurl.'/media/jui/js/jquery-noconflict.js']);
unset($doc->_scripts[$this->baseurl.'/media/jui/js/bootstrap.min.js']);
unset($doc->_scripts[$this->baseurl.'/media/jui/js/jquery-migrate.min.js']);
b) if the tooltip script is still included, it's probably inserted by JHtml::_('behavior.tooltip'); somewhere in the used component.
Lastly, never never never modify Joomla core files. it's a worst-practice.
PS.
For those who mentioned that the tooltip script is inserted but they don't find it in the $doc, that's because the $doc doesn't contain inline scripts.
I have already meet this issue, I am using Joomla 3. If it is your case then you can solve it by doing this :
Joomla 3 comes with jQuery on board, so by adding Joomla yourself, this may generate the issue.
Also make sure you include your jQuery over <jdoc:include type="head" />. If you necessary wants to include it. But I do not recommend this.
Hope this helps
I had the same problem when I was building a Joomla template/site with only HTML5, CSS3 and some small jQuery plugins for effects. It was unworthy of including heavy Bootstrap just to show some tooltips which I also didn't use at all.
Althought I already unset media/jui/js/bootstrap.min.js from JDocument but these lines of code
jQuery(document).ready(function(){
jQuery('.hasTooltip').tooltip({"html": true,"container": "body"});
});
were still appended by libraries/cms/html/bootstrap.php. So I got error "function tooltip not found".
I solved that by adding an empty function to my template's JS file.
jQuery.fn.tooltip = function (option) { };
There's a Joomla Plugin available which unsets Bootstrap and also removes the tooltip function snippet wich is inserted by JHtml::_('behavior.tooltip'); somewhere in any component.
See here: Disable Bootstrap Plugin
Create a system plugin with the follow code.
The First foreach loop unsets the .js file(s) added to the head. And the next foreach loop unsets the js code injected inside <script> tags.
There are two separate properties containing the scripts the _script & _scripts
public function onBeforeCompileHead() {
// Front end
if ($this->app instanceof JApplicationSite) {
$doc = JFactory::getDocument();
$search = array(
// 'jquery',
// 'caption.js',
// 'bootstrap.min.js',
// 'core.js',
// 'keepalive.js',
// 'punycode.js',
// 'validate.js',
// 'calendar.js',
// 'calendar-setup.js',
// 'mootools-more.js',
// 'modal.js',
// 'ajax-chosen.min.js',
// 'tabs-state.js',
// 'frontediting.js',
// 'html5fallback.js',
// 'jui/js/bootstrap.min.js',
// 'jquery.min.js',
'jui/js/',
'system/js/',
// 'text/javascript'
);
foreach ($doc->_scripts as $key => $script) {
foreach ($search as $findme) {
if (stristr($key, $findme) !== false) {
unset($doc->_scripts[$key]);
}
}
}
foreach ($doc->_script as $key => $script) {
if (stristr($key, 'text/javascript') !== false) {
unset($doc->_script[$key]);
}
}
}
}
I'm attempting to make a template file for a CMS that I'm making where the template file can contain variables like {username} as regular text that get replaced when the page gets included on the index.php page.
Example:
Index Page:
<?php include('templates/123/index.php'); ?>
templates/123/index.php page
<?php include('header.php'); ?>
Welcome {username}
<?php include('footer.php'); ?>
I've tried several methods; however, always run into problems because the page I'm trying to change the content on includes PHP code. Every method I try either 1) messes up because the opening and closing of PHP tags within the document OR 2) just echoes out the PHP code in the document. Is there any way that I can still achieve this? Maybe even with a class of some kind? I just want to be able to achieve this safely.
I will also be using this to where custom variables like {content1} get replaces with a php code that will be ioncubed that retrieves the data from database for content located in column1, same with {column2} {column3} and {column4}. I'm just trying to make the creation of templates extremely easy. (so I'd like to make the code work for that as well)
My preferred method of doing stuff like this involves starting my code with:
ob_start(function($c) {
$replacements = array(
"username"=>"Kolink",
"rank"=>"Awesome"
);
return preg_replace_callback("/{(\w+)}/",function($m) use ($replacements) {
return isset($replacements[$m[1]]) ? $replacements[$m[1]] : $m[0];
},$c);
});
Two steps I suggest
Load the result of your file "templates/123/index.php" into a variable. see this link for how to do it assign output of execution of PHP script to a variable?
use strtr() function to replace your placeholder i.e {username} with actual values
I think this will server your needs.
Situation:
A PHP Templating system that is responsible for building a pages HTML.
Javascript core functions are all in external files
Each Template has some default Javascript Functions that need to be called on a per/template basis
When A page is rendered, I next need to call a set of Javascript functions: i.e.
<script type="text/javascript">
$(document).ready(function{
API.loadThis(); // all these Javascript functions are in an external JS file
API.loadThat();
API.buildDateSelector("#idForSelector");
// etc
});
</script>
Up until now I have just appended that as text to each of the HTML templates. However, in the case of a Page that consists of multiple smaller Templates (each possibly containing their own initialization Javascript), then I have multiple bits of inline Javascript thrown all over my webpage.
My question is:
how should I properly organize everything so that I can easily "register" or "trigger" some default Javascript to be called upon page load?
Or is appending each block of Javascript to each template (like above) appropriate?
What I would do is quite similar to #Chris. However, I'd suggest a few minor changes:
Add a parameter to the addJS function which indicates the position on the page. By default you should have support for at least head and foot (head would place it in the head, foot would place it right before the closing </body>).
public function addJS($string, $position = 'head') {
if (!is_array($this->js[$position])) {
$this->js[$position] = array($string);
} elseif (!in_array($string, $this->js[$position])) {
$this->js[$position][] = $string;
}
}
Then, include tokens in the template to indicate the positions:
{{js_head}}
</head>
<body>
<!--content here-->
{{js_foot}}
</body>
Then, when rendering, just do something like:
$js = $this->js;
$positions = preg_replace_callback(
'/{{js_(inline_)?([a-zA-Z0-9]+)}}/',
function ($match) use ($js) {
if (isset($js[$match[2]])) {
$js = implode("\n", $js[$match[2]]);
if ($match[1] == 'inline') {
return $js;
} else {
return '<script type="text/javascript">'.$js.'</script>';
}
return '';
},
$templateBody
);
Now, the real benefit is that your templates can cleanly and trivially define their own positions for re-used and commonly used bits:
$this->addJS('return this.form.submit();', 'submit_form');
$html = '
<input type="text" onblur="{{js_inline_submit_form}}" />
<button name="submit" onclick="{{js_inline_submit_form}}" />
';
It can be quite useful since now you're not duplicating the JS calls everywhere in your code. Plus, it'll reduce the overhead of wrapping each output in <script> tags (since it wraps the entire position in the tags, rather than each piece of content)...
This would allow you to then take all of the non-inline JS and compile a series of files at run-time to send to the browser to take care of caching. It adds the benefit of being able to keep your JS close to your views (for maintainability) yet still serve cached JS and not have to re-send it every time...
public function buildJSCache($position) {
if (!isset($this->js[$position]) || empty($this->js[$position])) {
return '';
}
$file = implode($this->js[$position]);
$name = 'js/'.md5($file) .'.js';
if (!file_exists($name)) {
file_put_contents($name, $file);
}
return $name;
}
Then, in your template code, just do:
$replace = $this->buildJSCache('head');
if ($replace) {
$replace = '<script type="text/javascript" src="'.$filename.'"></script>';
}
$template = str_replace('{{js_head}}', $replace, $template);
You get the double-win of maintainability and speed to the user (you could even minify it if you wanted).
Note: all of this code is demonstration only, if you were to use it in production, I'd clean it up and think it out a bit further...
That's my $0.02 at least...
There are a lot of ways to do this. I suggest that you get some open source frameworks, look at how they do things and decide on your preferences. Some techniques are considered "best practice", but a lot of a framework's structure boils down to developer preference.
I have a framework I use for my own projects that is similar to what you describe, each of the smaller templates is termed as a "component". Each component is able to add any amount of javascript to itself using an addJS() method, likewise for css and html. A top-level controller loops through a given page's content (in terms of components). This give me a chance to have all the css, javascript, and html loaded ahead of time. Then, I can output them in whatever order I see fit.
So:
page controller handles request
inits one or more components
component load method populates html, javascript, and css class properties (arrays of file names, string for html)
for itself
components has its own set of templates, js, css
outputs site-wide templates
includes all component css within page header
iterates through components, outputs component html, layout stuff
outputs component JS for page footer
I have a components folder, within it are folders for each component. Within each component folder is a php file (that component's handler), optionally one or more css/js files. This keeps everything organized nicely.