I am new to Zend FW. I am looking to write a simple feedparser in a controller named Feedparsercontroller's indexAction. but i want to display the parsed feed output as a widget on my index page. how can i drive the output/ variable data to my indexview?
The below is my parser.
class FeedparserController extends Zend_Controller_Action {
public function init() {
/* Initialize action controller here */
}
public function indexAction() {
$feedUrl = 'http://feeds.feedburner.com/ZendScreencastsVideoTutorialsAboutTheZendPhpFrameworkForDesktop';
$feed = Zend_Feed_Reader::import ( $feedUrl );
$this->view->gettingStarted = array ();
foreach ( $feed as $entry ) {
if (array_search ( 'Getting Started', $entry->getCategories ()->getValues () )) {
$this->view->gettingStarted [$entry->getLink ()] = $entry->getTitle ();
}
}
}
}
i want to implement the same with my login , register controllers as well.
Perhaps I'm not understanding your question fully.
But, it seems the best approach here would be to create a separate feed controller that is solely responsible for the business logic associated with feeds (retrieving, massaging, setting to view, etc).
Then, create a partial which contains javascript code to call the feed controller, which then outputs the widget you're desiring. This does a few things very well.
It centralizes feed-related logic
It allows you to place the feed widget wherever you want
It is a SOA approach which is generally a good thing
Hope this helps!
I think the best logic with widgets is ajax.
Use some js widgets libraries (maybe jQuery ui for example), then make these widgets be loaded by some ajax queries, returning HTML, this allow you as well simple widgets reloading behviours (without relaoding the whole page).
In the server Side you'll need to allow your controller/Action to be called via ajax requests and to send only html snippets (not a whole page with all the layout).
To do that check ContextSwitch and AjaxContext Action Helpers. You will tell your FeedparserController that the index action can be called with /format/html in an XMLHHTTPRequest, and that in this case the view helper will be index.
In the init part you will say the indexAction can be called in ajax mode, rendering html snippets ('html'):
$Ajaxcontext = $this->_helper->getHelper('AjaxContext');
$Ajaxcontext->addActionContext('index', 'html')
->initContext();
Now simply rename your view script feedparser/index.phtml to feedparser/index.ajax.phtml
In the indexAction, do your stuff and output what you want on your view script, do not think about layout composition problems, you're working alone with your own layout part and the composition is done on the js side.
In the javascript part, ensure you're calling via ajax ($.load or $.ajax with jQuery maybe) the url with format/html added as parameters (so http://example.com/feedparser/index/format/html)
Note that in my opinion you should use json responses and not html, maybe json with some html inside. But that's a matter on how you want to control your ajax communication (and handle errors, redirection and such, and it's another subject).
What about a view helper ?
You can read about it View Helpers in Zend Framework
Related
I am currently involved in the development of a larger webapplication written in PHP and based upon a MVC-framework sharing a wide range of similarities with the Zend Framework in terms of architecture.
When the user has logged in I have a place that is supposed to display the balance of the current users virtual points. This display needs to be on every page across every single controller.
Where do you put code for fetching sidewide modeldata, that isn't controller specific but needs to go in the sitewide layout on every pageview, independently of the current controller? How would the MVC or ZF-heads do this? And how about the rest of you?
I thought about loading the balance when the user logs in and storing it in the session, but as the balance is frequently altered this doesn't seem right - it needs to be checked and updated pretty much on every page load. I also thought about doing it by adding the fetching routine to every controller, but that didn't seem right either as it would result in code-duplication.
Well, you're right, having routines to every controller would be a code-duplication and wouldn't make your code reusable.
Unlike suggested in your question comments, I wouldn't go for a a base controller, since base controllers aren't a good practice (in most cases) and Zend Framework implements Action Helpers in order to to avoid them.
If your partial view is site-wide, why don't you just write your own custom View Helper and fetch the data in your model from your view helper? Then you could call this view helper directly from your layout. In my opinion, fetching data through a model from the view doesn't break the MVC design pattern at all, as long as you don't update/edit these data.
You can add your view helpers in /view/helpers/ or in your library (then you would have to register your view helper path too):
class Zend_View_Helper_Balance extends Zend_View_Helper_Abstract
{
public function balance()
{
$html = '';
if (Zend_Auth::getInstance()->hasIdentity()) {
// pull data from your model
$html .= ...;
}
return $html;
}
}
Note that you view helper could also call a partial view (render(), partial(), partialLoop()) if you need to format your code in a specific way.
This is a pretty simple example, but to me it's enough is your case. If you want to have more control on these data and be able to modify it (or not) depending on a particular view (or controller), then I recommend you to take a look on Placeholders. Zend has a really good example about them here on the online documentation.
More information about custom view helpers here.
When you perform such a task, consider using the Zend_Cache component too, so you won't have to query the database after each request but let's say, every minute (depending on your needs).
What you are looking for is Zend_Registry. This is the component you should use when you think you need some form of global variable. If you need this on EVERY page, then you are best adding it to your bootstrap, if you only need it in certain places add it in init method of relavent controllers.
application/Bootstrap.php
public _initUserBalance()
{
$userId = Zend_Auth::getInstance()->getIdentity()->userId;
$user = UserService::getUser($userId);
Zend_Registry::set('balance', $user->getBalance());
}
application/layouts/default.phtml
echo 'Balance = ' . Zend_Registry::get('balance');
That wee snippet should give you the right idea!
In this case, I usually go with a front controller plugin with a dispatchLoopShutdown() hook that performs the required data access and adds the data to the view/layout. The layout script then renders that data.
More details available on request.
[UPDATE]
Suppose you wanted to display inside your layout the last X news items from your db (or web service or an RSS feed), independent of which controller was requested.
Your front-controller plugin could look something like this in application/plugins/SidebarNews.php:
class My_Plugin_SidebarNews
{
public function dispatchLoopShutdown()
{
$front = Zend_Controller_Front::getInstance();
$view = $front->getParam('bootstrap')->getResource('view');
$view->sidebarNews = $this->getNewsItems();
}
protected function getNewsItems()
{
// Access your datasource (db, web service, RSS feed, etc)
// and return an iterable collection of news items
}
}
Make sure you register your plugin with the front controller, typically in application/configs/application.ini:
resource.frontController.plugins.sidebarNews = "My_Plugin_SidebarNews"
Then in your layout, just render as usual, perhaps in application/layouts/scripts/layout.phtml:
<?php if (isset($this->sidebarNews) && is_array($this->sidebarNews) && count($this->sidebarNews) > 0): ?>
<div id="sidebarNews">
<?php foreach ($this->sidebarNews as $newsItem): ?>
<div class="sidebarNewsItem">
<h3><?= $this->escape($newsItem['headline']) ?></h3>
<p><?= $this->escape($newsItem['blurb']) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
See what I mean?
I am in the process of learning the MVC pattern and building my own lightweight one in PHP
Below is a basic example of what I have right now.
I am a little confused on how I should handle AJAX requests/responses though.
In my example user controller below, If I went to www.domain.com/user/friends/page-14 in the browser, it would create a User object and call the friends method of that object
The friends method would then get the data needed for the content portion of my page.
My app would load a template file with a header/footer and insert the content from the object above into the middle of the page.
Now here is where I am confused, if a request is made using AJAX then it will call a page that will do the process over, including loading the template file. IF an AJAX call is made, I think it should somehow, just return the body/content portion for my page and not build the header/footer stuff.
So in my MVC where should I build/load this template file which will have the header/footer stuff? ANd where should I detect if an AJAX request is made so I can avoid loading the template?
I hope I am making sense, I really need help in figuring out how to do this in my MVC I am building. IUf you can help, please use some sample code
/**
* Extend this class with your Controllers
* Reference to the model wrapper / loader functions via $this->model
* Reference to the view functions via $this->view
*/
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
public function load($model){
//load model
//this part under construction and un-tested
$this->$model = new $model;
}
}
user controller
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/page-14
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriends();
$this->view->load('userFriends', $friendsData);
}
}
For me, I developed a separate object that handles all template display methods. This is good because you can then ensure that all the resources you need to display your UI is contained in one object. It looks like you've isolated this in Core_View.
Then, when an AJAX call is made, simply detect that it is an AJAX call. This can be done by either making the AJAX call through an AJAX object, which then references other objects, or you can take an easy approach and simply set an extra POST or GET field which indicates an AJAX call.
Once you've detected if it's an AJAX call, define a constant in your MVC such as AJAX_REQUEST. Then, in your template/UI object, you can specify that if it's an AJAX call, only output your response text. If it isn't, proceed with including your template files.
For me, I send it through an AJAX object. That way I don't have to worry about making a single output work for both cases. When it's ready to send a response, I just do something to the manner of print( json_encode( ...[Response]... ) ).
well, it would all start with normal request which would load the initial page. there are many options as to handle this but let's say that you start with /users/friends page which would list all your friends. then each of the friends should have link to specific friend's profile -- now this is the moment where ajax could kick in and you could ajaxify links to your friend profiles - this means that instead of normal you would instead use let's say jQuery and setup click handler in a such way that
$("a").click(function(){$.post($(this).attr("href"), null, function(data){$("#content").html(data);}});
this would use "href", and upon click would make post request to your backend. at backend, if you see that it's post, then you would just return the content for that particular friend. alternatively, if you have get request, you return all - header - content - footer.
if you use technique above, make sure to properly handle the data you receive. e.g. if there are further actions that should be done via ajax, make sure to "ajaxify" the data you get back. e.g. after updating html of the content, again apply the $("a").click routine.
this is just trivial example, to kick you off, but there are many more sophisticated ways of doing that. if you have time, I suggest reading some of agiletoolkit.org, it has nice mvc + ajax support.
You will need to use a different view. Maybe something like:
funciton friends() {
$this->view = new Ajax_Request_View();
$friendsData = $this->model->getFriends();
$this->view->load($friendsData);
}
I'm loading javascript files in the bootstrap as usual, but there's a file that I want to have included only if it's a page that has a form
->appendFile('http://myurl.com/js/formscript.js');
Is there a way to detect the page being loaded, from the bootstrap so I can decide whether or not to include this file?
I thought about passing a variable from the Form to the view, and then checking for that variable in the bootstrap, but it's not working.
This would be in my form
$layout = new Zend_Layout();
$view = $layout->getView();
$view->formscript = true;
and this would be in my bootstrap
if ($view->formscript)
but var_dump($view->formscript) give me null, so any other ideas to activate js files only in specific conditions?
To include javascript files in a particular pages alone, add the following code in those pages(I mean view scripts - *.phtml).
<?php
$this->headScript()->appendFile('http://myurl.com/js/formscript.js');
?>
Similarly, to add CSS files to a particular page, do the following.
<?php
$this->headLink()->appendStylesheet('http://myurl.com/styles.css');
?>
It is possible, but you do not need your bootstrap. You can just access the variable from your layout:
//form
$view = Zend_Layout::getMvcInstance()->getView();
$view->formscript = TRUE;
//layout
if($this->formscript)
{
$this->headScript()->appendFile('http://myurl.com/js/formscript.js');
}
echo $this->headScript();
Do not use getView() in your form as it will return the view object for the form, not for your application. This has tripped me up more than a couple of times >.>
Your idea to set a flag - something like $view->hasForm - in your view seems like a pretty reasonable approach. But as others have noted, it shouldn't be the form itself that attempts to set the flag since it doesn't really have access to view object until rendering time.
Instead, wherever you place a form into your view - probably in a controller, perhaps even in a front controller plugin - simply set your flag there.
Then your view script or layout can call $this->headScript()->appendFile() if the flag has been set.
Why not move over appendFile() to your form class (of course if you use Zend_Form), you would be sure that your JS line will be created only in the same time as your form. The place for this line is good in init() as well as in render()
class Your_Form extends Zend_Form {
public init(){
$this->getView()->appendFile('http://myurl.com/js/formscript.js');
[...]
}
}
Is there a way to load a controller from a view ?
Here is what i am affter..
I want to use one view multiple times, but this view is being loaded by separate controller that gives the view, information from the db.So becouse of that information from the model i can't just set $this-load->view(); and etc. Is there a way to do this thing, or it has a better way ?
I think a lot of sites face similar challenges, including one I'm working on that loads the same db content into the sidebar on almost every page in the site. I implemented this with the combination of a library and a helper:
Put the data logic into the library (mine is named common.php). In addition to interfacing with the database, you may want the library to store the data in a local variable in case you want to reference it multiple times on a single load.public function get_total_items()
{
if ($this->_total_items === NULL)
{
$row = $this->ci->db->query("SELECT COUNT(*) FROM items")->row();
$this->_total_items = $row[0];
}
return $this->_total_items;
}
Create a helper to load the library. (Don't load libraries within a view!) I have MY_text_helper that loads the library and returns the data:function total_items()
{
$CI =& get_instance();
return $CI->common->get_total_items();
}
Call the helper function from within the view.<p> Total items: <?php echo total_items(); ?> </p>
Simply put, you can't and shouldn't load a controller from a view. That sad, I understand your frustration because you want to re-use the model-pulling/acting logic in the controller across multiples views.
There are various ways of doing this;
Re-use the models. Your models should be very simple to select data from, and should be sleek, but if you're doing the same thing over and over it does seem stupid. In which case...
Use a controller as a "main container" and extend upon it from any logic you need. So your basically using the controller as a template, which pulls data down from the model, loads the appropriate view.
MVC doesn't work that way ... Just re-use the model - that's why it's separate from the controller. If that doesn't fit your needs, you should probably implement a library that does the logic.
I would use a library.
That way you can wrap up the data retrieval in a reusable package that you can call from any controller you like.
just do this
if you controller named controller1
put a link in view just like that
http://your-site.com/index.php/controller1/
if you want specific function add it to your url
http://your-site.com/index.php/controller1/myfunction
that's it
I am currently building a small admin section for a website using Zend Framework, this is only my second time of using the framework so I am a little unsure on something things. for example are I have an archive option for news articles where the user will hopefully click a link and the article will be archived however I cannot work out how to get this to run without having a view?
this is my controller
public function archiveNewsAction()
{
//die(var_dump($this->_request->getParam('news_id')));
$oNews = new news();
$this->_request->getParam('news_id');
$oNews->archiveNewsArticle($news_id);
//die(var_dump($oNews));
$this->_redirect('/admin/list-all');
}
and this is my model
public function archiveNewsArticle($news_id)
{
//die($news_id);
$db = Zend_Registry::get('db');
$sql = "UPDATE $this->_name SET live = '0' WHERE news_id = '$news_id' LIMIT 1";
die($sql);
$query = $db->query($sql);
$row = $query->fetch();
return $row;
}
I would appreciate any help any one can give.
Thanks
Sico
I use this with calls to AJAX-only actions that I either don't want output or I'm using some other output, like XML or JSON:
// Disable the main layout renderer
$this->_helper->layout->disableLayout();
// Do not even attempt to render a view
$this->_helper->viewRenderer->setNoRender(true);
This has the added benefit of no overhead of redirection if what you are doing has no output/non-HTML output.
To disable view rendering in an action (put this in the specific action. If you want it for the entire controller put it in the init method):
$this->_helper->viewRenderer->setNoRender();
If you are using the layout component of ZF also add this:
$this->_helper->layout->disableLayout();
I could not figure out your code there. in your model you are calling die(). why?
it will stop the execution. are you sure about that line? anyway, if you have a controller in Zend Framework and do not need any view, you can turn the view off by this line:
// code in your controller
$this->_helper->viewRenderer->setNoRender(true);
// the rest of the controller
now the controller will not search for a view script to show to the user.
make sure you will call
$this->_redirect()
after all of your controller job is done.
Orignal Answer:
Your call to:
$this->_redirect();
Calls the Redirector action helper, which (unless you've configured it not to) will automatically exit the script as soon as the headers are written, so the view will never be called or rendered, there's no need for a view script.
Follow-up Answer:
In order to call the action without sending the user to the other "page" and then redirecting back again you'll need to use an XMLHttpRequest (AJAX) call. These links should provide the information you need:
http://developer.mozilla.org/en/AJAX
http://www.ibm.com/developerworks/xml/library/wa-ajaxintro1.html
http://www.oracle.com/technology/pub/articles/schalk-ajax.html
Also take a look at some JS frameworks that make using XMLHttpRequest cross-browser much easier:
http://www.prototypejs.org/
http://mootools.net/
Zend Framework actually has built in support for the Dojo JS framework, which you may find easier:
http://framework.zend.com/manual/en/zend.dojo.html
http://www.dojotoolkit.org/