In Symfony I have a line include_component('core', 'ohrmList'); that prints a table. But I am unable to find the for loop that generates the table.
I want some conditional formatting on the table. Should I go and edit in exact for loop or is there any way to do this?
You can read about components here Chapter 7 - Inside The View Layer
I think you must find module core, and in templates folder,file _ohrmList.php
Components are like "little" controllers. Their goal is pretty much the same as partials' but you use components when you need to do some extra action which would normally not fit into the view layer (e.g. a database call, some complicated computations etc.).
You can find the functions of the components in your module's actions directory in the components.class.php file.
Each component renders a partial named after its' own name. So the ohrmList component will render the _ohrmList.php partial after running the executeOhrmList() function.
In your case you should look inside the core module.
You can format component output based on values passed to it.
For example, you can render you view with
include_component('core', 'ohrmList', array('name' => 'Maria', 'status' => 'approved') )
and in _ohrmList.php you can do something like
<?php echo $name ?> - <?php if ( $status == 'approved' ): ?>congrats, you were approved!!!<?php endif; ?>
Related
I've got the following situation. As in your typical ZF2-Application there is existing a layout.phtml-view-script for the layout and a view-script specific to the called action. In my case it's team/frontend/index.phtml.
My problem is concerned with the headtitle. The headtitle-parts are currently set directly within the view-script-files, like the following:
layout.phtml
<?php
echo $this->headtitle('My Application')->setAutoEscape(false)->setSeparator(' | ');
team/frontend/index.phtml
<?php
$this->headtitle('Our team');
This is - as intended - resulting in My Application | Our team. So far so good.
Now I am writing a new module, which - beside some other features - also should provide the possibility for some SEO-stuff. One of the SEO-tasks is to overwrite the title of a the inner view script: team/frontend/index.phtml but not for layout.phtml. The new title is coming from the database, however this is not relevant for this problem.
So for this action I want to be able to produce an outcome like this: My Application | The faces behind the team. As you can see I only want to overwrite the everything the action-view-script sets, but not the title part of the layout.phtml.
Since it's a completly different module, which would add this functionality and both modules should work independendly I hope this is solvable through events/the EventManager.
Sidenote: the new module is called: Node
I tried 2 things, both resulting in the same thing:
I attached to the MvcEvent::EVENT_RENDER EventManager within the onBootstrap-method of my Node\Module.php and (in another attempt) I fetched EventManager of Zend\View\View and attached to the ViewEvent::EVENT_RENDERER_POST.
In the callback-function I fetched the title from the database and set it by fetching the HeadTitle-View-Helper.
Both attempts resulted in a final headtitle of My Application | Our team | The faces behind the team meaning the parts were just appended although I used the SET-Parameter within the callback function.
This is a simplified version of the callback-function:
$viewHelperManager = $serviceLocator->get('viewhelpermanager');
// Get new title-part from database
$titlePart = '...' // In this case "The faces behind the team"
// Set HeadTitle
$headtitle = $viewHelperManager->get('headtitle');
$headtitle($node->getNodeName(), 'SET');
As you can see here I am using SET as the second parameter. I do understand, why it's not working: it's too late the event seems to be triggered when the action-view-script and the layout-view-script are finished processing. However I need a possibility to hook in before the layout-view-script is processed, so that I can overwrite the action-view-scripts' headtitle.
I hope you understand what I mean.
Any thoughts ideas on this? Is there an event which is triggered for every view-script in the queue?
UPDATE 2015-10-14 - 13:10
I've further investigated the code and the event triggering within the code of ZF2. Because of the structure in which it is written, my request is just not possible in the way I wanted it to be.
Like Wilt's and akond's anwers the headtitle should be generally dealt with in the action or in particular other places, however not within the view-script itself.
Wilt posted the link to the ZF2-docs concerning headtitle. There they provide an example of how to set the headtitle within an action.
I knew how to do that, however in the Album-Module Tutorial (http://framework.zend.com/manual/current/en/user-guide/database-and-models.html#listing-albums) they set the headtitle within the view, so I went this way...
Of course, it's no problem for me to fix my other modules (like my Team-Module), however I will run into problems with vendor-modules. If authors of other module keep setting their headtitles within their view-scripts, my Node-Module won't stand a chance. The only thing I could do in that case is to overwrite their view-scripts and remove the setting of the headtitle...
In my opinion you are experiencing this problem because of contamination of the view with data. In other words, you should not have put neither 'My Application' or 'Our team' into the view.
What you should be having instead is a model/helper, that provides view with an appropriate head title. Something along the lines of:
$this->view ()->headtitle ('Our team');
in the controller action and
echo $this->headtitle
in the view.
View should only render data that is provided by model. In our case, the view is a model in its own right. That's bogus.
You should set your head title in your controller not in your view.
Check the ZF2 documentation on head title to see how to properly use this view helper:
$headTitle = $viewHelperManager->get('headTitle');
$headTitle->setSeparator(' | ');
$headTitle->append('My Application');
$headTitle->append('Our team');
In the view only:
<?php echo $this->headTitle() ?>
Outputs the following html:
<title>My Application | Our team</title>
In your other module, in the other controller you can set new variables for the headscript.
Another possibility (don't think it is better) would be to pass your titles as variables to your view in a parameter. For example like this:
$view = new ViewModel(array(
'titles' => array(
'layout' => 'My Application',
'action' => 'Our team'
)
));
$view->setTemplate('template');
return $view;
Now in the views you can do:
$this->headtitle($this->titles['layout'])->setAutoEscape(false)->setSeparator(' | ');
and
$this->headtitle($this->titles['action']);
This last solution is more dirty, because you are not supposed to set your head title like that in the view if you ask me. But it will work and it suits your current solution more (less refactoring).
UPDATE
If you are afraid of others overruling the title you set in their views then you can also extend the the HeadTitle view helper and overwrite the '_invoke' method so it does not allow the overwriting the $title if it is already set.
And then re-register (overwrite) it in the view helper manager:
'view_helpers' => array(
'invokables' => array(
'headTitle' => 'My\Custom\View\Helper\HeadTitle',
)
)
In our setup, we have models+mappers for all db objects. Then there are controller actions which prepare model objects for respective actions based on business logic.
We have send entire model object to the view and if view (html) wants to show first name, it can call $obj->getFirstName() or if some other view (pdf) can even call $obj->getFullName(). Is this how it is supposed to be done?
What if country was left empty and the view ignorantly calls $obj->getCountry()->getISO3Code() will be fatal since getCountry() returned false instead of a expected country object.
One option is to bother the view with IF.. etc so it is made safe. but does it not defeat the purpose that views should be dump without logic? or maybe I over stressed it.
should we send the entire model object to the view (as now) or safely prepare and send a array of viewable fields? It kinda it makes the action to be aware how PDF view looks like and html view looks like, again maybe defeating controllers purpose.
I confess that I struggle with the same question. When the controller/action sets values in the view - $this->view->someKey = 'someValue' - then there is an implicit expectation that that the controller is aware of what the view requires. I guess the general idea is that this is ok; the view is responsible for how to render the data it is passed.
There is nothing wrong with using if statements inside your view-scripts. It is pretty common to see something like:
<?php if ($someCondition): ?>
<!-- some markup here -->
<?php endif; ?>
in a view-script. For example, take a look at the partials associated to a pagination control.
I have created view-model objects - kind of a read-only version of my model intended for use in a view - that permits me to do things in a view-script that are a bit cleaner. For example, you could have a view-model object with a method like hasCountry(), so that your view-script could do something like:
<?php if ($viewmodel->hasCountry()): ?>
<p>Country: <?= $model->getCountry()->getISO3Code() ?></p>
<?php endif; ?>
Kind of a trivial example, but for more complex logic about the entity I am trying to render, I find that a view-model like this provides a home for some of that rendering-specific logic that doesn't feel right in the controller and seems a bit complex for a view-script.
Quick question about general MVC design principle in PHP, using CodeIgniter or Kohana (I'm actually using Kohana).
I'm new to MVC and don't want to get this wrong... so I'm wondering if i have tables:
categories, pages, notes
I create a separate controller and view for each one...? So people can go to
/category/#
/page/#
/note/#
But then lets say I want to also be able to display multiple notes per page, it would be bad to call the note view in a loop from the page view. So should I create some kind of a function that draws the notes and pass variables to that function from the note view and from a loop in the page view? Would this be the best way to go about it, if not how else should I do it...?
Thanks,
Serhiy
Yes, instead of just passing 1 entity (category, page, note) to your view, pass a list of entities. With a loop inside the view, you can display the whole list.
That view may call another one (or a function) that know how to display one entry.
I would personally have a "show" method for one item and a "list" method for multiple. In your controller you can say something like $page_data['note'] = get_note(cat_id,page_id) for the "show" method and $page_data['notes'] = get_all_notes(cat_id) for the "list" method.
Then in your view, you loop over the $page_data['notes'] and display HTML for each one. If the list view is using the same "note" HTML as the "show" view, create a template or function to spit out the HTML given a note:
// In your "list" view
foreach($n in $page_data['notes']){
print_note_html($n)
}
//In your "show" view
print_note_html($n)
The print_note_html function can be a helper method accessible by all views for Notes. Make sense?
You can loop in the View. The View is allowed can also access the model in MVC. See: http://www.phpwact.org/pattern/model_view_controller
You don't need to have a controller (or model) for each table.
In CodeIgniter I create a separate helper file where I put functions that return the markup for UI elements that may need to be included multiple times in the one view.
In your example, I would create a function to return the markup for a note.
application/helpers/view_helper.php
function note($note)
{
return '<div class="note">' .
'<h2>' . $note->title . '</h2>' .
'<p>' . $note->contents . '</p></div>';
}
I would normally auto-load this helper file. And then in the view I would do something like this.
echo note($note);
For a list of notes in a view, I would iterate the list calling this function.
<div class="note-list">
<?php foreach ($notes as $note) : ?>
<?php echo note($note); ?>
<?php endforeach; ?>
</div>
I found that including a view many times in another view was slow. Thats why I did it this way.
Edit
I just dug into the CodeIgniter Loader class and sure enough a PHP include is being done every time you call
$this->load->view('view_name');
This means that if you use this method to display a list of 20 notes, you're going to be doing 20 separate includes.
I'm using CodeIgniter, and will likely use their template library as I want to keep things extremely simple to use. The content for the template variables will come from the database, but I want the business admins to know what content areas are available. Basically the names of the parameters when they choose a specific template. For instance, Joomla uses an extra XML file that defines each area, whereas Wordpress uses comments within a page template to inform the system that the PHP file is a template. I like the Joomla approach because you don't have to parse the PHP file to find the areas, but I like the Wordpress approach because you don't have an extra XML file associated with every template. Are there other approaches that I'm missing?
I think the nicest way would be to add a small hack to the template parser class. The code looks quite readable and clean in system/libraries/Parser.php. You could insert a hook in that class that can be used to keep track of the variables. I don't know, if it works, but here's a snippet:
class CI_Parser {
var $varCallback;
function setVarCallback($callbackFunction) {
$this->varCallback = $callbackFunction;
}
...
function _parse_single(...) {
$callback = $this->varCallback;
$callback($key);
}
...
//Somewhere in your code
function storeVarName($variableName) {
// Persist the variable name wherever you want here
}
$this->parser->setVarCallback('storeVarName');
You could do this directly in the controller:
// in the controller
print_r($data);
$this->load->view("main", $data);
Or a little more rudimentary, but you could pass to the template a PHP array of variables (or an object):
// in the controller
$data = array();
$data["namespace"] = array(
"title" => "My website",
"posts" => array("hi", "something else")
);
$this->load->view("main", $data);
And then in the view, have a flag to print_r the namespace to show all the vars available, so that business admins know exactly what to use.
// in the view
if(isset($namespace["showAllVars"])) print_r($namespace);
One option would be to call token_get_all on the PHP file (only when your business admins are loading it up), and parse the output of that.
The best approach, in my opinion, is to keep the variable definitions in another place (such as a database table, or a separate file). This will help with testing (i.e., a programmer can't just remove a tag and it's gone) and making sure things are still working as you move on with the application development in time.
Another advantage is that your application logic will be independent from the templating engine.
On a side note, if you expect a lot of traffic, you may consider using smarty instead. We have done extensive testing with most of the templating engines around and smarty is the fastest.
I have a CakePHP application that in some moment will show a view with product media (pictures or videos) I want to know if, there is someway to include another view that threats the video or threats the pictures, depending on a flag. I want to use those "small views" to several other purposes, so It should be "like" a cake component, for reutilization.
What you guys suggest to use to be in Cake conventions (and not using a raw include('') command)
In the interest of having the information here in case someone stumbles upon this, it is important to note that the solution varies depending on the CakePHP version.
For CakePHP 1.1
$this->renderElement('display', array('flag' => 'value'));
in your view, and then in /app/views/elements/ you can make a file called display.thtml, where $flag will have the value of whatever you pass to it.
For CakePHP 1.2
$this->element('display', array('flag' => 'value'));
in your view, and then in /app/views/elements/ you can make a file called display.ctp, where $flag will have the value of whatever you pass to it.
In both versions the element will have access to all the data the view has access to + any values you pass to it. Furthermore, as someone pointed out, requestAction() is also an option, but it can take a heavy toll in performance if done without using cache, since it has to go through all the steps a normal action would.
In your controller (in this example the posts controller).
function something() {
return $this->Post->find('all');
}
In your elements directory (app/views/element) create a file called posts.ctp.
In posts.ctp:
$posts = $this->requestAction('posts/something');
foreach($posts as $post):
echo $post['Post']['title'];
endforeach;
Then in your view:
<?php echo $this->element('posts'); ?>
This is mostly taken from the CakePHP book here:
Creating Reusable Elements with requestAction
I do believe that using requestAction is quite expensive, so you will want to look into caching.
Simply use:
<?php include('/<other_view>.ctp'); ?>
in the .ctp your action ends up in.
For example, build an archived function
function archived() {
// do some stuff
// you can even hook the index() function
$myscope = array("archived = 1");
$this->index($myscope);
// coming back, so the archived view will be launched
$this->set("is_archived", true); // e.g. use this in your index.ctp for customization
}
Possibly adjust your index action:
function index($scope = array()) {
// ...
$this->set(items, $this->paginate($scope));
}
Your archive.ctp will be:
<?php include('/index.ctp'); ?>
Ideal reuse of code of controller actions and views.
For CakePHP 2.x
New for Cake 2.x is the abilty to extend a given view. So while elements are great for having little bits of reusable code, extending a view allows you to reuse whole views.
See the manual for more/better information
http://book.cakephp.org/2.0/en/views.html#extending-views
Elements work if you want them to have access to the same data that the calling view has access to.
If you want your embedded view to have access to its own set of data, you might want to use something like requestAction(). This allows you to embed a full-fledged view that would otherwise be stand-alone.
I want to use those "small views" to
several other purposes, so It should
be "like" a cake component, for
reutilization.
This is done with "Helpers", as described here. But I'm not sure that's really what you want. The "Elements" suggestion seems correct too. It heavily depends of what you're trying to accomplish. My two cents...
In CakePHP 3.x you can simple use:
$this->render('view')
This will render the view from the same directory as parent view.