What's the best way to create a temporary variable inside a .phtml template while abiding by Magento's architecture?
Example
File: /template/catalog/product/view.phtml
<?php
$myVar = $_product->getAttributeText('color');
if ( empty($myVar) ) {
// does not exist
} else {
// show the attribute
}
?>
Beyond this expression $myVar isn't needed anywhere else.
Note: I'm not looking for alternative ways to write this code that avoid creating vars. For the sake of argument, assume a scenario where creating a temporary var is necessary.
What should $myVar be?
$myVar
$namespaced_myVar
$_myVar
Magento's registry pattern http://alanstorm.com/magento_registry_singleton_tutorial
Something else...
Looking for a "real world" solution more than a purist answer. How would you write this?
Answer
Combined between Ben's answer and this bit from Alan/Vinai's conversation https://twitter.com/VinaiKopp/status/225318270591442945 — this is how I'm going to write it:
If the anything more than basic logic is needed, I'll extend the class with new methods.
Otherwise, I'll create new vars in the local scope like so:
$mynamespace_myVar = 'xyz';
This is what I like about it:
The $mynamespace_ reminds me I created this and not Magento
It also makes it highly unlikely another developer overwrites my vars
This is what I don't like:
It's un-pure and potentially corruptible, but I probably only need this <5 times for an entire site so it's reasonably shielded.
Not using $_ to show the var is local to this template is not "the Magento way" but it makes the code more readable.
So my templates will mostly have code like this:
$gravdept_someNiceData = true;
Some history: https://stackoverflow.com/a/3955757/833795
Re 1, 2, & 3: Differentiating between these choices involves getting into "purist answer" territory, as they are all local variables.
Using the registry pattern is uncalled for, as the desired scope is stated to be local to the rendering of the template.
Based on your example, an appropriate construction in Magento might be:
<?php if ($_product->getColor()): ?>
<h2> I HAZ COLOR </h2>
<?php else: ?>
<h2> I NO HAZ COLOR </h2>
<?php endif ?>
If there is anything but the simplest test of return values it's appropriate to add this logic as a method of the block class (using a rewrite), as it is in fact view logic which you've mentioned should be local to this context only.
Related
In a Laravel 4.2 setup, I have a variable in a template that I wish to share across multiple includes:
master.blade
<?php $tabindex = 0; ?>{{--This is the variable--}}
#include('header'){{-- <-in header.blade, I often use ++$tabindex --}}
{{--$tabindex is still 0--}}
#include('content'){{-- <-in content.blade, I often use ++$tabindex --}}
{{--$tabindex is still 0--}}
#include('footer'){{-- <-in footer.blade, I often use ++$tabindex --}}
{{--$tabindex is still 0--}}
$tabindex if used as a tabindex html attribute is clearly a trivial example that I can get around with safety values and a large enough buffer value, but that's hardly elegant or a solution to the actual problem at hand. In regular php includes, it's my understanding that variable assignment in included files would affect the variables in the including file - this is the desired effect.
I tried View::share(), but it presented the same symptoms. Passing the value to the #include as an array is clearly passing by value and produced the same effect as well.
It almost seems like the including scope values are evaluated first in their entirety, and then the included scopes. If this is the case, it would make what I'm trying to do much less feasible if there is any usage in the including scope or further included scopes (even storing by way of some persisting memory) because the order of execution would be different than the order in the code.
Is there some undocumented blade sorcery to prevent a blade #include from cutting itself off from changing the values of its includer's variables or must I fall back on straight php include or some other ugly alternative (Session variables should persist their values across calling include scopes, but that's just a nasty and flimsy approach)?
Using,
#include('view', array('key'=>'value'))
Would be the best way.
I take it from what you said, that you've been doing something like this.
View::share('some_variable',$some_variable);
And maybe initialized the variable in the template. This practice is discouraged, but there-s another way you can do it, which would be to initialize the variable in a php file and share it from there by adding this line to the file.
$some_variable = 0; //Initialize it any way you need to.
View::share('some_variable', $some_variable);
And then in your app/start/global.php you add this line.
require app_path().'/composers.php';
Laravel blade include seem to create a variable scope for every included template you add.
View:share('name', $value)
Does different thing from what you want, it is intended to inject some arbitrary variables to every template rendered, it's usefull to define assets path in bootstrap or entry point of your controller.
To solve your problem, just tell php in the included scope to look up for a variable above via global, so main.blade.php:
<?php $tabIndex = 0 ?>
#include('subform');
and in templates subform.blade.php
<?php
global $tabindex;
$tabindex++;
?>
Note, this might not work if you define the variable not in a main template, I have tried this only at main template (the one that I render to in controller) and it worked.
I am building a website using php. I would want to separate the php from the html. Smarty engine, I guess does that, but right now its too complicated for me. Looking for a quick fix and easy to learn solution, one which is an accepted standard as well. Anyone helping please.
Consider frameworks or choose a template engine
Use a framework. Depending on your project, either a micro framework like Slim or something more complete like Laravel.
What I sometimes do when writing complex systems with quite much php code is separating it the following way (don't know your exact project, but it might work for you):
You create a php file with all the functions and variables you need. Then, you load every wepgage through the index.php file using .htaccess (so that a user actually always loads the index.php with a query string). Now, you can load the html page using file_get_contents (or similar) into a variable (I call this $body now); this variable can be modified using preg_replace.
An example: In the html file, you write {title} instead of <title>Sometext</title>
The replacement replaces {title} with the code you actually need:
$body = str_replace('{title}', $title, $body);
When all replacements are done, simply echo $body...
Just declare a lot of variables and use them in the template:
In your application:
function renderUserInformation($user)
{
$userName = $user->userName;
$userFullName = $user->fullName;
$userAge = $user->age;
include 'user.tpl.php';
}
In user.tpl.php:
User name: <?=$username?><br>
Full name: <?=userFullName?><br>
Age: <?=$userAge?>
By putting it in a function, you can limit the scope of the variables, so you won't pollute your global scope and/or accidentally overwrite existing variables.
This way, you can just 'prepare' the information needed to display and in a separate php file, all you need to do is output those variables.
Of course, if you must, you can still add more complex PHP code to the template, but try to do it as little as possible.
In the future, you might move this 'render' function to a separate class. In a way, this class is a view (a User View, in this case), and it is one step in creating a MVC structure. (But don't worry about that for now.)
Looking for a quick fix and easy to learn solution
METHOD 1 (the laziest; yet you preserve highlighting on editors like notepad++)
<?php
// my php
echo "foo";
$a = 4;
// now close the php tag -temporary-
// to render some html in the laziest of ways
?>
<!-- my html -->
<div></div>
<?php
// continue my php code
METHOD 2 (more organized; use template files, after you passed some values on it)
<?php
// my php
$var1 = "foo";
$title = "bar";
$v = array("var1"=>"foo","title"=>"bar"); // preferrable
include("template.php");
?>
template.php
<?php
// $var1, $var2 are known, also the array.
?>
<div>
<span> <?php echo $v["title"]; ?> </span>
</div>
Personally, i prefer method 2 and im using it in my own CMS which uses lots and lots of templates and arrays of data.
Another solution is of course advanced template engines like Smarty, PHPTemplate and the likes. You need a lot of time to learn them though and personally i dont like their approach (new language style)
function renderUserInformation($user)
{
$userName = $user->userName;
$userFullName = $user->fullName;
$userAge = $user->age;
include 'user.tpl.php';
}
I'm having trouble with using PHP code on my pages, within my body content area. I've searched tirelessly on this site, Drupal's site, and other sites in general, so I apologize if the answer is on this site somewhere, but I can't find it and I need help.
We have a lot of info we reuse throughout our site that I'd like to store in a PHP file as variables. We do this on our site now, but I'm rewriting our whole site to use Drupal. So, for example, we sell software, and I'd like a variable for each of our product URLs for various 'add to cart' buttons on the site. I don't want to have to hardcode the product URL into each link, but rather to seta PHP variable that I can call on any drupal page.
I cannot get anything to work; I've read about several suggestions but none work. I've tried setting the variables as their own block, then calling them from within a page when I create a new page. I can echo the variables on the pages but only within the block they are inside, I cannot call them and get them to echo from other blocks or content areas. I've tried using the global keyword (as per one suggestion) but that didn't work for me.
I hope this makes sense. Other info? I'm using Drupal 6.x, I do have PHP code enabled when creating pages, I do have the PHP filter module enabled, I can get PHP code to render so I know it's working, it's just not working where I need it to be.
I should say (if it's not obvious just from reading this!) I am a Drupal newbie so if anyone can help and try to explain their suggestion as plainly as possible for me, I'd really appreciate it!
Thanks in advance.
EDIT 3/15/11
To try to explain further, I'll post some sample code. I haven't done this yet because there isn't much to show yet, and I thought it might confuse the issue even more.
So, I've made a Drupal 'page' which is for our software trial downloads. The PHP variables that I want to set are for our download links; I want to set them in one place so that if, in the future, the download link needs to change, I only have to do so in one spot. You see, we have download links on various site pages. The same is true of our 'buy now' links. Here is the page code:
<p>Try [product] free for 30 days.</p>
<!--<p>[token_custom_ewintry]</p>-->
<p><?php global $ewintry; ?>Download for Windows PC</p>
<p><?php global $emactry; ?>Download for Mac OS X</p>
<p><?php global $ebbtry; ?>Download for BlackBerry</p>
<?php
$ebbtryprint = variable_get("ebbtry", "default");
print $ebbtryprint;
?>
<p>Download for Windows Mobile</p>
<p><?php global $ipewlstorelink; ?>iPhone, iPad, iPod touch owners: Download [product] on the iTunes App Store. You'll be able to create 10 cards for free to try [product] before you buy!</p>
For this sample I've left in everything I've tried. You'll see my calls to global variables, which never worked. I have the global variables defined in a custom block that I created and placed in my 'content top' region. I learned that apparently nothing from that region is actually accesible to my page's body content, because the calls never worked.
I have a custom token that I made yesterday with the Tokens module; it never worked, but then I read on a different post that by default, tokens are available in the body content area, and I need a special filter. I've yet to find a filter, and so I am not sure this solution will ever work.
I have my call to variable_get. Now, this did work. I have variable_set defined within my template.php page. My value does print using the print call above in my code sample. However, I looked at this page this morning and I don't think that's the answer I need. Because now I'll have to call variable_get on all my pages before I can print anything, right? And that doesn't solve the problem where I wanted to only have to set everything in one place to call anywhere. I tried putting the variable_get call in my custom block, but again I can't access anything in 'content top' from my body content area. The variable_get call prints the value in 'content top' but then it will not re-print below that in the content area.
So maybe that code will help someone to help me. I am going to look in detail at CCK now, as that's the only other suggestion I haven't tried. Thanks in advance if anyone can help.
If you're trying to set a global variable, and then use it within a function/method block, you need to use the global keyword on import:
<?php
// For some reason, this sometimes gives me problems
$foo = 'test';
// So I do this instead, they are equivalent
$GLOBALS['bar'] = 'test';
echo "<p>Global <br/> foo: $foo <br/> bar: $bar</p>";
function globalTest() {
global $foo;
echo "<p>globalTest() <br/> foo: $foo <br/> bar: $bar</p>";
}
globalTest();
function globalBarTest() {
global $foo, $bar;
echo "<p>globalBarTest() <br/> foo: $foo <br/> bar: $bar</p>";
}
globalBarTest();
?>
In action: http://jfcoder.com/test/globals.php
Prints:
Global
foo: test
bar: test
globalTest()
foo: test
bar:
globalBarTest()
foo: test
bar: test
I have always gotten in the habit of setting a global variable using $GLOBALS, I never have any issues doing it this way.
I would caution, though, that setting globally scoped variables is considered harmful (or at least unnecessary), since they are so easy to accidentally overwrite somewhere else in your code (by you and/or someone else).
Your stated approach in the description sounds quite messy; you should be using a database and let Drupal abstract how you organize, set and get the data from the datastore, instead of editing your files and hardcoding some links and data into a PHP file. This is what I'm thinking reading your description, which may not be fair, but I thought I needed to mention it.
EDIT
In Drupal, you can set global variables in the default/settings.php page using variable_set(), and then use variable_get() to get the variable by name.
http://api.drupal.org/api/drupal/sites--default--default.settings.php/6
variable_set('foo','bar');
http://api.drupal.org/api/drupal/includes--theme.inc/function/template_preprocess/6
function yourtemplate_preprocess (&$variables) {
$vars['foo'] = variable_get('foo');
}
EDIT 2
Note the source for the variable_set() function:
<?php
function variable_set($name, $value) {
global $conf;
$serialized_value = serialize($value);
db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", $serialized_value, $name);
if (!db_affected_rows()) {
#db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, $serialized_value);
}
cache_clear_all('variables', 'cache');
$conf[$name] = $value;
}
?>
EDIT
Ok, here is what you can do:
/drupal-root/htdocs/sites/settings.php
Open the settings.php file and at the bottom, set your PHP variables using the $GLOBALS global variables, as so:
$GLOBALS['test1_variable'] = 'test 1 variable';
And then in your template (with the PHP Input Format selected):
<?php
echo "<p>This is my {$GLOBALS['test1_variable']}.</p>";
?>
Or...
<p>This is my short tag <?=$GLOBALS['test1_variable'];?>.</p>
And you should see your variable printed out on the page from the template code. Note the curly braces surrounding the $GLOBALS variable.
If you want to provide additional info that should go with some nodes, you should use CCK to create a content type, that has all the additional infos. With styling inside a template, you can archive almost anything.
If CCK is not siutable (in most cases, it is exactly what you want), you need to implement a _preprocess_ function. This would look like
function yourtemplate_preprocess_page(&$variables) {
$vars['my_custom_var'] = "hello"; //anything can go here
}
Now you have a $my_custom_var in your page-template file available.
Be sure to make yourself familiar with the template system of Drupal (if you haven't already).
It sounds like you are looking for the token_filter module.
My problem is that it isn't ready (in token module) for D7 yet.
I am building a English/french website and was wondering if there is a best practice for such a job.
Duplicating the site and making a french and english folder with the appropriate site inside.
Using PHP to swap the content with html tags.
eg. if($lang=='en'):
Use php to swap only the content leaving the html tags the same for both. eg. if statements all over the place. Would this be bad for efficiency?
Any other suggestions would be appreciated
We have a framework in place for when (if) our site goes international that works like this...
Folder structure;
/
lang/
english/
images/
text/
dutch/
images/
text/
Any text or images that are language specific are removed from the page directly and replaced by constants. eg On the login screen, we drop in;
echo TEXT_LOGIN_WELCOME;
which is defined in /lang/english/text/login.php as;
define('TEXT_LOGIN_WELCOME', 'Welcome, please login:');
but in /lang/dutch/text/login.php it's defined as;
define('TEXT_LOGIN_WELCOME', 'Welcome, please login (in dutch):');
;-)
Each language define file is named exactly the same as the page it is used for, so when we load a public-facing page, we only need to figure out which language the user speaks and we can include the relevant language define file.
The good thing about this system is that all the language info is centralised. When you need to add a new language, simply copy the main (english?) folder, rename it, zip the whole thing up and send it to a translation service to work their magic. Of course, the downside of this system is the maintenance as both languages and content grow... If anyone has any bright ideas with regard to this then I'd love to hear them!
Btw, if you end up needing to guess a user's location by IP, you might want to check out geoIP.
Use a templating system. Smarty Template Engine is probably one of the most well-known PHP ones. Not only does a templating system serve the exact purpose you're trying to accomplish, it also makes maintaining pages far easier by separating the display code from the content (which also allows you to use the same template for lots of different content pages of a similar nature).
As the simplest way I recommend you to use i18n internationalization method & gettext catalogs (.po files).
The famous WordPress project is using it as well.
1 - Duplicating the entire site will force you to repeat every code touch-up into the 2 folders :-[
2 - If you mean somenting like
<?php if($lang=='en') { ?>
<p>English text</p>
<? } else { ?>
<p>Text français</p>
<? } ?>
This solution is perfect to manage two languages in the same page.
But you still have duplicated tags.
3 - Change only content it's really satisfaction.
Maybe proliferate of if statements can weigh down php compiling... I don't know.
Anyway document can be more concise with this approach:
<?php
function interpreter($buffer) {
$pieces = explode('#', $buffer);
if (isset($_GET['lang'])) $begin=$_GET['lang'];
else $begin = 1; // 1 to display français, 2 to display english
$tot = count($pieces);
for ($i=$begin; $i<$tot; $i+=3) { // remove one language
unset($pieces[$i]); }
foreach ($pieces as $value) { // recompose the text
$output .= $value; }
return $output;
}
ob_start("interpreter");
?>
#Français#English#
<p>#English text#Texte français#.</p>
<?php ob_end_flush() ?>
The text between ob_start and ob_end_flush is parsed AFTER php compiling.
That means are affected strings coming eg. from echo statement, not inside < ?php ?> tags.
Also content coming from php include IS affected.
But NOT external css or javascript.
Keep attention delimiter # isn't a caracter yet used elsewhere.
Maybe you'll prefer to replace with || or ^^
Of course in the future you can adapt this solution into 3 languages or more. But if you have to insert the "Third language translation#" in many lines of a big site, maybe the solution from MatW fits you.
I want to define something like this in php:
$EL = "\n<br />\n";
and then use that variable as an "endline" marker all over my site, like this:
echo "Blah blah blah{$EL}";
How do I define $EL once (in only 1 file), include it on every page on my site, and not have to reference it using the (strangely backwards) global $EL; statement in every page function?
Most PHP sites should have a file (I call it a header) that you include on every single page of the site. If you put that first line of code in the header file, then include it like this on every page:
include 'header.php';
you won't have to use the global keyword or anything, the second line of code you wrote should work.
Edit: Oh sorry, that won't work inside functions... now I see your problem.
Edit #2: Ok, take my original advice with the header, but use a define() rather than a variable. Those work inside functions after being included.
Sounds like the job of a constant. See the function define().
Do this
define ('el','\n\<\br/>\n');
save it as el.php
then you can include any files you want to use, i.e
echo 'something'.el; // note I just add el at end of line or in front
Hope this help
NOTE please remove the '\' after < br since I had to put it in or it wont show br tag on the answer...
Are you using PHP5? If you define the __autoload() function and use a class with some constants, you can call them where you need them. The only aggravating thing about this is that you have to type something a little longer, like
MyClass::MY_CONST
The benefit is that if you ever decide to change the way that you handle new lines, you only have to change it in one place.
Of course, a possible negative is that you're calling including an extra function (__autoload()), running that function (when you reference the class), which then loads another file (your class file). That might be more overhead than it's worth.
If I may offer a suggestion, it would be avoiding this sort of echoing that requires echoing tags (like <br />). If you could set up something a little more template-esque, you could handle the nl's without having to explicitly type them. So instead of
echo "Blah Blah Blah\n<br />\n";
try:
<?php
if($condition) {
?>
<p>Blah blah blah
<br />
</p>
<?php
}
?>
It just seems to me like calling up classes or including variables within functions as well as out is a lot of work that doesn't need to be done, and, if at all possible, those sorts of situations are best avoided.
#svec yes this will, you just have to include the file inside the function also. This is how most of my software works.
function myFunc()
{
require 'config.php';
//Variables from config are available now.
}
Another option is to use an object with public static properties. I used to use $GLOBALS but most editors don't auto complete $GLOBALS. Also, un-instantiated classes are available everywhere (because you can instatiate everywhere without telling PHP you are going to use the class). Example:
<?php
class SITE {
public static $el;
}
SITE::$el = "\n<br />\n";
function Test() {
echo SITE::$el;
}
Test();
?>
This will output <br />
This is also easier to deal with than costants as you can put any type of value within the property (array, string, int, etc) whereas constants cannot contain arrays.
This was suggested to my by a user on the PhpEd forums.
svec, use a PHP framework. Just any - there's plenty of them out there.
This is the right way to do it. With framework you have single entry
point for your application, so defining site-wide variables is easy and
natural. Also you don't need to care about including header files nor
checking if user is logged in on every page - decent framework will do
it for you.
See:
Zend framework
CakePHP
Symfony
Kohana
Invest some time in learning one of them and it will pay back very soon.
You can use the auto_prepend_file directive to pre parse a file. Add the directive to your configuration, and point it to a file in your include path. In that file add your constants, global variables, functions or whatever you like.
So if your prepend file contains:
<?php
define('FOO', 'badger');
In another Php file you could access the constant:
echo 'this is my '. FOO;
You might consider using a framework to achieve this. Better still you can use
Include 'functions.php';
require('functions');
Doing OOP is another alternative
IIRC a common solution is a plain file that contains your declarations, that you include in every source file, something like 'constants.inc.php'. There you can define a bunch of application-wide variables that are then imported in every file.
Still, you have to provide the include directive in every single source file you use. I even saw some projects using this technique to provide localizations for several languages. I'd prefer the gettext way, but maybe this variant is easier to work with for the average user.
edit For your problem I recomment the use of $GLOBALS[], see Example #2 for details.
If that's still not applicable, I'd try to digg down PHP5 objects and create a static Singleton that provides needed static constants (http://www.developer.com/lang/php/article.php/3345121)
Sessions are going to be your best bet, if the data is user specific, else just use a conifg file.
config.php:
<?php
$EL = "\n<br />\n";
?>
Then on each page add
require 'config.php'
the you will be able to access $EL on that page.