Passing database results into header file in PHP-MVC - php

I'm starting a new project using the http://www.php-mvc.net framework but have never had to include database results in the header file before and not sure how to go about it. I need to pull a list of current categories and there ID's from the database and use them to populate a menu.
The header.php file is in /views/_templates. The normal way of passing database results to the view is to run the query in the relevant model, get the data in the controller, pass the data from the controller to the view, then loop over the data with a foreach loop in the view. The problem being the _template files don't have any kind of controller for them.
The best I can come up with is to use include and include a view file from the home controller, using that controller to get the results and pass them to a menu.php in the views/home folder.
/views/_templates/header.php
<li class="dropdown">
<?php include 'views/home/menu.php'; ?>
</li>
/views/home/menu.php
foreach ($links as $link){
<li><?php echo $link->name; ?></li>
}
the above code has been shortened its more for principle than a working example.
The method I have come up with works but I wanted to know if there's a more elegant way of doing things?

As I commented:
No need to store in session you could use a Twig global so its available in all templates and you could make sure that your base controller runs a preExceute to pull all the data together and then add that global.
That might look something like this:
abstract class MyBaseController extends Controller
{
private $view = null;
private prepareView()
{
$twig_loader = new Twig_Loader_Filesystem(PATH_VIEWS);
return new Twig_Environment($twig_loader);
}
public function getView()
{
if ($this->view === null) {
$this->view = $this->prepareView();
}
return $this->view;
}
protected function preRender()
{
// whatever logic you need to prepare the menu data as $links
$this->getView()->addGlobal('links', $links);
}
public function render($view, $data_array = array())
{
$this->preRender();
// render a view while passing the to-be-rendered data
echo $this->getView()->render($view . PATH_VIEW_FILE_TYPE, $data_array);
}
}
Now depending on what data you need to build your stuff for $links you may or may not need to get a bit more elaborate. Especially given how the URL params are handled in the Application class. I really hope you're only doing this as a learning experience because this "framework" you've found isn't really good for much other than learning how you might implement MVC.

Related

Injecting PHP Variables into HTML Code

This has probably been asked before, but I couldn't find the answer to my question with some preliminary searches, so here it is:
A very simple example of my current method of injecting PHP variables into HTML is as follows:
HTML (file.php):
<tag><?php echo $variable; ?></tag>
PHP:
$variable = "value";
ob_start();
include "file.php";
$results = ob_get_clean();
This achieves the correct result just fine, but it annoys me every time that I have to copy and paste those three lines to get variables injected into my HTML files. There's a chance I might want to change the details of this injection functionality at a later date, and it's currently scattered in a couple hundred locations throughout my code.
In a perfect world, I would move those three lines to a separate function that takes the file path as an input, and returns the "compiled" result. However, the function would then no longer have access to the calling scope, and I would have to pass the variables in as another input. The only thing I'm able to think of for doing that is:
function injectVariables($filePath, array $variables)
{
//Built-in PHP function, extracts array key => value pairs into variable $key = value pairs in the current scope
extract($variables);
ob_start();
include $filePath;
return ob_get_clean();
}
That certainly gets the job done, and it will be what I implement if there aren't any better solutions, but it seems less than ideal to me. It involves creating an array every time this runs, just to loop through it and extract all the variables, which just feels a bit wrong to me.
Does anyone know of any other methods that might work?
Not really sure what you are asking, but here is my answer
I don't know the structure of your code, but I hope you are using a MVC approach (or at least something which deals with classes) so that you can do the following:
In your main controller, you create a class variable like viewData which will be an array. And you put in it everything you want
$this->viewData['username'] = $username;
$this->viewData['myArray'] = array('foo' => 'bar');
$this->viewData['menuSubview'] = 'path_to_menu_subview.php';
Then, you create a render function.
public function render()
{
extract($this->viewData);
ob_start();
include("myfile.php");
return ob_get_clean();
}
And in myfile.php (with the HTML) you can simply do what you used so far
<div id="menu"><?php include($menuSubview);?></div>
<p><?=$username;?></p>
<p><?=$myArray['foo'];?></p>
The whole code can be something like this.
class Something {
protected $viewData;
protected $viewFile;
public function logic()
{
$this->userProfile();
echo $this->render();
}
public function userProfile()
{
$this->viewData['username'] = 'John The Ripper';
$this->viewFile = 'myFile.php';
}
public function render()
{
extract($this->viewData);
ob_start();
include($this->viewFile);
return ob_get_clean();
}
}
Here's a simplified class that stores data and allows for recursive rendering of files that all have access to the save data form the initial instance.
class View {
// view data
protected $_data = [];
/**
* Set view data
*/
public function __set( $key, $value )
{
$this->_data[ $key ] = $value;
}
/**
* Get view data
*/
public function __get( $key )
{
if( array_key_exists( $key, $this->_data ) )
{
return $this->_data[ $key ];
}
return null;
}
/**
* Render view file
*/
public function render( $file )
{
if( is_file( $file ) )
{
$view = $this;
ob_start();
include( $file );
return ob_get_clean();
}
return 'File not found: '.$file;
}
}
Just use the variable $view inside your included files to access the data in the class, or render() another partial file that can do the same thing, and so on.
// Bootstrap a View instance and add some data
$view = new View;
$view->dataOne = 'valueOne';
$view->dataTwo = 'valueTwo';
// Render main template
echo $view->render( 'template.php' );
Inside template.php
<header>
<?= $view->render( 'header.php' ) ?>
</header>
<h1><?= $view->dataOne ?></h1>
<p><?= $view->dataTwo ?></p>
Nothing is wrong with your injectVariables() function, it is in fact a good idea.
You shouldn't have performance impact anyway (If this is your main concern your are doing premature optimization !)
Your view (HTML) shouldn't know about the internal of your application. (You split responsability - this is a huge subject that I won't talk deep)
You know if something end-up into $variables it has been build/formatted or is revelent to be display for $filePath.
In complex system you may end up with a variable with a pdf generator, why would the HTML want that? The only purpose of the HTML is to DISPLAY HTML data.
Some variables from your logic will end-up in $variables almost every time such as session informations (Who is currently logged), for your case this is normal.
In the perfect world if you put a class into $variables it should be designed only for purpose of your HTML even if it is almost the same object as your logic.
As an exemple I designed a Session class in my logic. It does have method such as Logout() and EnsureIsAlive() as well as field such as FullName. FullName is going to be use by my HTML because I want to display it in the header of my page.
I should have a Session class for the HTML that only contains a FullName field because the HTML has only one job and is it to display data. Login() and EnsureIsAlive() are specific for the logic and aren't related at all with displaying data.
In reallity time is always a constraint and because you are writing all by yurself from scratch you may end-up just sending the logic Session class into $variables.
Design note : I'm a C# programmer and we always use class over array to pass parameters as a good pratice and it does affect the exemple about how to pass FullName to your HTML. In your case, instead of creating a class dedicated for the HTML you could also create a simple array :
$variables['Session'] = array('FullName' => $mySession->fullName).
That way you still avoid your HTML to have access to the unrelated method specific for your logic. I'm not sure if it is a good pratice in php...
Keep context variables clean
If you do call injectVariables() more than once (for different $PathFile) you may not want the same variables.
One injectionVariables() could be for a widget, another for a complete complete page.
Why would a TimeWidget need your totalInvoice variable?
You also have the ability to prepare multiples $variables at the same time because each injectVariables() will have his own parameters.
I'm not up-to-date but I know phpBB template system use (back near 2000) the exact same pattern as your injectVariables()).
You wrote that each of your page do call injectVariables(), you could also (but it may be complex) do it only in one file - like php frameworks.
Framework handle HTTP requests into one specific file
(eg. http://yoursite.com/hello/world or http://yoursite.com/login would call (internaly) http://yoursite.com/index.php?page=hello&subpage=world and http://yoursite.com?page=login).
Then this page (index.php in my exemple) would include a specific file (controller) according with his GET parameters ($_GET['page'] and $_GET['subpage']).
The controller job would be to fetch data, apply logic then fill $variables.
Then index.php will call injectVariables() with the releated HTML.

CakePHP: how to do sub-actions with sub-views

Apologies if I'm using the wrong terminology to describe what I'm trying to do...
I have a model/controller called Report which users can view like so:
example.com/reports/view/123
Each report hasAndBelongsToMany File objects. I need to make those files accessible like so:
example.com/reports/view/123/file/456
Or
example.com/reports/view/123/456
^ ^
| |
report file
I'm intentionally NOT creating a separate action for files (example.com/files/view...) because access to the file is relative to the report.
What is the correct way to do this in CakePHP?
My first guess is to add logic inside of ReportsController::view that checks for the existence of the second parameter (file) and manually render() a different view (for the file) conditionally. But I'm not sure if this is "the CakePHP way".
You are in the right path, modify your action to accept an optional parameter.
public function view($file = null) {
$somethingElse = null;
if (isset($file)) {
//your logic
$somethingElse = $this->Foo->bar();
}
$this->set(compact('somethingElse'));
}
Regarding to the view, I don't know your requirements, but I think you don't need to create a different view, you can either put a conditional in your view to show something, or (my favourite approach) create an element that will be displayed only if $somethingElse contains something. That is:
//View code
if (!empty($somethingElse)) {
echo $this->element('yourAwesomeElement', compact('somethingElse'))
}
Then in yourAwesomeElement
foreach ($somethingElse as $something) {
echo $something;
}
The good thing is that your element will be reusable for future views that could need this.

PHP, understanding MVC and Codeigniter

I'm trying to understand MVC, and learning CI framework. I've some questions about MVC and some basic questions about CI.
1)Views are visual part of application as i read from tutorials, my question is: e.g There is a button "Login" but if user already logged in button will be "Logout". Where will that login check be? On controller or on view? i mean
//this is view//
<?php if($_SESSION('logged') == true):?>
Logout
<?php else: ?>
login
<?php endif; ?>
or
//this is controller//
if($_SESSION('logged') == true)
$buttonVal = 'logout';
else
$buttonVal = 'login';
//and we pass these value to view like
$this->view->load('header',$someData);
//this time view is like
<?=$somedata['buttonVal']?>
i just write theese codes as an example i know they wont work, they are imaginary codes, but i guess you got what i mean. Login check should be on controller or on view?
2)Should models contain only codes about data and return them to controller? For example there is a math, we get 2 value from database and multiply them and display them. Model will multiply or controller will do it?
here we load data with model and do math on controller:
//model
$db->query(....);
$vars=$db->fetchAll();
return $vars;
//controller
$multi = $vars[0] * $vars[1];
$this-load->view('bla.php',$mutli);
here we load data with model and do math on model too, controller just passes data from model to view:
//model
$db->query(....);
$vars=$db->fetchAll();
$multi = $vars[0] * $vars[1];
return $multi;
//controller
$multi = $this->model->multiply();
$this-load->view('bla.php',$mutli);
i mean with that, models should do only database works and pass data to controllers, controller do rest of work and send view to render? Or models do work, controllers get them and send them to view?
3)This is about codeigniter, i have a header which has to be in every page, but it has javascripts,css depending to page i'm using
<?php foreach ($styles as $style): ?>
<link id="stil" href="<?= base_url() ?>/css/<?= $style ?>.css" rel="stylesheet" type="text/css" />
<?php endforeach; ?>
this will be on every page, so in every controller i have
$data['styles'] = array('css1','css2');
$this->load->view('header', $headers);
i'm thinking to make a main controller, write this in it, and all my others controllers will extend this, i see something MY_Controller on CI wiki, is this MY_Controller same with what i'm doing? Are there any other ways to do this?
Sorry for bad English and dummy questions. Thanks for answers.
This is absolutely view logic, the correct way to do it in my opinion:
<?php if($logged_in):?>
Logout
<?php else: ?>
login
<?php endif; ?>
The value of $logged_in would probably be retrieved from a call to a library method:
<?php if ($this->auth->logged_in()): ?>
Authentication is one of those things you'll want access to globally, so you may be calling $this->auth->logged_in() in controller or views for different reasons (but probably not in models).
In every controller i have
$data['styles'] = array('css1','css2');
$this->load->view('header', $headers);
Yes you could extend the controller class with MY_Controller, but you're better off keeping this in the view/presentation layer. I usually create a master template:
<html>
<head><!-- load assets --></head>
<body id="my_page">
<header />
<?php $this->load->view($view); ?>
<footer />
</body>
</html>
And write a little wrapper class for loading templates:
class Template {
function load($view_file, $data) {
$CI = &get_instance();
$view = $CI->load->view($view_file, $data, TRUE);
$CI->load->view('master', array('view' => $view));
}
}
Usage in a controller:
$this->template->load('my_view', $some_data);
This saves you from loading header/footer repeatedly. In my opinion, presentation logic like which CSS file to load or what the page title should be belongs in the view whenever possible.
As far as models go, you want them to be reusable - so make them do what you need and keep it strictly related to data manipulation (usually just your database). Let your controller decide what to do with the data.
Not related to MVC, but in general you want to write as little code as possible. Redundancy is a sign that you could probably find a better solution. These are broad tips (as is your question) but hopefully it helps.
1) View logic should be simple and mostly if-then statements, if needed. In your example, either case would work but use the logic in the view. However, if you were checking for login and redirecting if not logged in, then that would occur in a controller (or a library).
2) Think of Codeigniter models as ways to access database functions - Create, Retrieve, Update, Delete. My (loose) rule of thumb is for Codeigniter models is to return results from update, delete or insert queries or a result set from a fetch query. Any applicable math can then occur in the controller. If this is a math operation that occurs EVERY time, consider adding it to a library function. See below...
3) Extending the controller is the proper and best way to accomplish this.
*) Not to add more to your plate, but also be sure to learn about Codeigniter Libraries. For example, in your controller you could load your library. You then call your library function from your controller. The library function calls a model which retrieves your database result. The library function then performs math on that function and returns the result to the controller. The controller is left with little code but a lot is accomplished due to the library and model.
The user lo-gin check should be in the controller.
This should be the first function that need to be invoked in the constructor.
Below i have given the sample code which redirects the user to the login page if he is not logged in, hope this would give you some idea,
<?php
class Summary extends Controller {
function Summary() {
parent::Controller();
$this->is_logged_in();
}
function is_logged_in() {
$logged_in = $this->session->userdata('logged_in');
if (!isset($logged_in) || $logged_in != true) {
$url = base_url() . 'index.php';
redirect($url);
exit();
}
}
?>
The button change can be implemented in the view by checking the session variable in view and making decisions accordingly.
Please take look at this link

MVC in PHP - How does the controller know the view?

I'm new to MVC in PHP and I was just wondering what the best way to do this is?
At the moment I've got a simple setup like this:
Model
User.php
Controller
controller.php
View
login.php
register.php
my_account.php
The model has all the database functionality for logging in and registering, and the view files have the relevant working forms.
My main question is, what is the best way to have the controller call pages? Currently, it looks something like:
public function show_page()
{
if ($_GET['p'] == "login")
{
include('View/login.php');
if (isset($_POST['username']))
{
$this->user->login($_POST['username'], $_['pass']
}
}
if ($_GET['p'] == "register") { include('View/register.php'); }
if ($_GET['p'] == "my_account") { include('View/my_account.php'); }
}
This doesn't seem logical, am I doing it wrong?
I think that the best way is to use some kind of routing system so you have a map somewhere with the possible url pattern / page to show combinations and after deciding which controller to call you can load the appropriate view in your controller.
What you presented here seems somewhat blurred to me. I think that you should check out implementations out there like Pure mvc or symfony so you can get a grip on the concept quickly. I believe that you (or anyone else for that matter) shouldn't reinvent the wheel but study, understand and improve what you can get.
If you are going to create your own MVC framework then you should check out the basic MVC concepts and plan your software before trying to write it.
Most PHP based MVC frameworks that I've used utilize a front controller. The .htaccess file directs all traffic to a single (usually index.php) file (usually) in the root of the project.
This file is responsible for determining which application controller to load. That controller is then responsible for any and all application level logic.
in a framework I wrote, in my front controller I do this
$page = tgsf_parse_url();
require resolve_controller( $page );
The tgsf_parse_url function in the above code parses $_SERVER['REDIRECT_URL'] to determine what variables are being passed.
the resolve_controller in the above code handles plugin hooks and 404 conditions, but the bottom line is that it always returns a path to send to include/require so that variable scoping doesn't become an issue (including in a function limits variable scope)
Any variables that are set in the controller are automatically available in a view when you include a view like this:
// this is an application level controller file
$name = 'Mr. Example';
include view( 'example' );
Then in the view file:
<h2><? echo $name; ?></h2>
What you have is not dynamic at all.
This is how I do it in my MVC:
private function loadView(){
//Bring the variables to the global scope
extract($this->getAll()); //load all variables into local scope
if(Config::get('view')){
$template_file = 'view/' . Config::get('view') . '/' . Config::get('method') . '.stp';
if(is_file($template_file)){
include $template_file;
}
else {
include 'view/missingview.stp'; //no such view error
}
}
else {
Config::set('template', 'blank');
include 'view/missingfunction.stp'; //no such function error
}
}
Have something set the view somewhere in the code.
This is how I set the view in the controller:
public function __construct(SessionManager $SESSION) {
$this->pageOn = Config::get('page');
$this->session = $SESSION;
$model_name = $this->name;
if(class_exists($model_name) && is_subclass_of($model_name, 'AppModel')){
/**
* #var AppModel $model_name
*/
$this->$model_name = new $model_name();
}
else {
//default model (no database table chosen)
$this->$model_name = new AppModel();
}
/* Get all posts */
$this->posts = $this->$model_name->getPosts();
Config::set('view', strtolower($model_name)); //<<RIGHT HERE!
if(!$this->session->get(strtolower($model_name))){
$this->session->set(strtolower($model_name), array());
}
}

Access Controller data from an element

I have an element for my side bar navigation being called from my layouts/default.ctp file, I need to access some data about categories from my Photos controller. How would I go about doing this?
Be careful with requestAction unless you make effective use of caching it can really slow down your application as it starts a whole new request cycle every time you call it.
Travis Leleu's answer would be the standard way of doing things. But, if you must import the data (for portability or whatever reason) into the element then requestAction is not the way to go.
As you are not performing any business logic that must be in the controller I strongly recommend you import and instantiate the model class as a singleton into your element. You can do this using ClassRegistry::init();
$Photo = ClassRegistry::init('Photo');
$Photo->find('all');
If you need to do any additional processing of the data you should do that in the model itself either using Cakes afterFind callback or making a custom method in your Photo model:
class Photo extends AppModel {
function customFind () {
$photos = $this->find('all');
foreach ($photos as $photo) {
//processing code here...
}
}
}
Then call it in your element:
$Photo = ClassRegistry::init('Photo');
$Photo->customFind();
Basically what I'm trying to get across here is the only situation where requestAction is appropriate is where you need to do things like redirects or using components.
If you a simply retrieving and/or manipulating data then it belongs in the model.
You can just regard your layous/default.ctp as a normal template,and put
<?php echo $this->element('your element'); ?>
where you need it.
b.t.w,use:
$data = $this->requestAction('controller/action');
to access the data
You can send data to your element. For example in your default.ctp:
<?php echo $this->element('side_nav', $your_data); ?>
and in your side_nav.ctp you can process that data.
Why wouldn't you just do it the standard Cake convention for this?
In the controller,
$this->set( 'categories', $this->Photos->find(...) );

Categories