How do I organize this list into mvc? - php

I want to make a list of Best selling prints in my store.
I have a table (print_for_sale) which has fk_sale_id and fk_print_id where I can see how many prints have been bought.
I came up with this code to print out an organized list, from most bought to least (not sure it is entirely correct):
<?php
$sql = "SELECT fk_print_id as printId, COUNT(print_for_sale_id) as saleCount
FROM print_for_sale
GROUP BY fk_print_id
ORDER BY saleCount DESC";
$result = mysql_query($sql);
while ($row = mysql_fetch_assoc($result)) {
echo $row['printId']; echo $row['saleCount']; }
?>
I'm trying to make a transition of this to my model, controller and view (mvc) but I'm having a lot of trouble. I'm new to this and I've tried in so many different ways, none of which seem to work. I'm lost. Right now I have it like this (and it prints out nothing) or the only thing it prints out is "Notice undefined variable arr" when I make an echo in my view
model.php
function getBestSelling() {
$sql = "SELECT fk_print_id as printId, COUNT(print_for_sale_id) as saleCount
FROM print_for_sale
GROUP BY fk_print_id
ORDER BY saleCount DESC";
$result = $this->conn->query($sql);
return $this->buildArr($result);
}
controller.php
function bestselling() {
$arr = $this->model->getBestSelling();
print_r($arr);
include 'view.php';
}
view.php
<?php echo $arr['printId'] ?>
I want to print out the result of the query but nothing is working. I'm sorry if this isn't an appropriate question but I could really use some help understanding this.
I'm not using any framework.
In my model I do have a class model { }, a function __construct() and a function __destruct(). I also have a function to build the arrays.
function buildArr($result) {
$arr = array();
while ($row = $result->fetch_assoc()) {
array_push($arr, $row);
}
return $arr;
}
In my controller I include 'model.php'; and have a class Controller {} and function __construct() { $this->model = new model();}.
I also have a index.php where I start the session and define("BASE_URL", 'http://' . $_SERVER['SERVER_NAME'] . '/printstore/index.php/');
The app is working. I can already register users, login and make purchases. I just can't seem to get this part of the code right.
Would appreciate some help! Sorry for the long post and thank you in advance.

The included file is not behaving as you expect it to. To pull this off you will have to create a global out of the variable (but don't do it!). You can print_r() the array in the controller, because you're fetching it from the model. But after that you include the view. When you place that array in an included file, without defining it in the file itself, you're not going to get a result because only the including file has access to any defined variables. Not the other way around.
So if you want this to work you're going to either have to make the variable $arr global (not recommended), or include the controller in view.php. (Note that this is not MVC!) If you want this to be MVC you'll have to use classes for the controllers and use those objects in the view. To find a better explanation for this please refer to the links down below.
I am personally fond of using of a templating engines (also not MVC related). I'm quite fond of Smarty, but I've heard good stories about mustache too.
These templating engines provide you with a way to pass variables to your templates, without including the controller file. For example in Smarty:
PHP:
$variable = 'Hello World!';
$smarty->assign('variable', $variable);
$smarty->display('helloworld.tpl');
Smarty, helloworld.tpl:
{$variable}
Output:
Hello World!
I'd also recommend you to read these, they might help you out more than I've been able to explain.
Setting up MVC in PHP: http://www.sitepoint.com/the-mvc-pattern-and-php-1/
Including files in PHP: Passing a variable from one php include file to another: global vs. not
Edit
Teresko is right that you should not be using globals. I just specified them here, because it's possible to use them (definitely not recommended).
Read this question for a couple of why's: Stop using `global` in PHP

Related

How to achieve an MVC view-like pattern without a framework

It's really convenient how MVC patterns allow you to define view a and then load variables into it via the controller. For the sake of argument let us take CodeIgniter as an example:
Controller:
Class Example extends CI_controller(){
function show_page(){
$data = array('msg'=>'Hello World');
echo $this->load->view('hello',$data);
}
}
View (hello.php):
<h1><?php echo $msg; ?></h1>
I have taken over an old project written years ago where there are redundant html code everywhere. It has no pattern whatsoever just straight up poorly structured code.
I wanted to create a class that has a function that will fetch all HTML code from a file in one folder, feed variables to them and show the result. Like so:
Folder structure:
View_folder
- hello.php
Class
- view_class.php`
Main:
<?php
$data['msg'] = 'Hello World!';
echo $view_class->get_view('hello.php',$data);
?>
Is it possible to achieve this? Can someone give an example function on how to do this. Thanks.
Sure thing, that's what the frameworks are doing. Here's a really basic overview of how I'd expect that to work:
function view($template, $data){
extract($data); // this pulls all of the first-level array keys out as their own separate variables
ob_start(); // this turns on **output buffering** which is the method we'll use to "capture" the contents of the view (there are surely other ways)
require $template; // you should prepend some sort of fixed path to this where all of your templates will reside
echo ob_get_flush(); // turns off output buffering, returning the buffered content as a string which we then send to the browser.
}

MVC simple select count from db and display explained

I am working with an MVC for the first time and developing a library app for personal dev. I am using the php login found here. I am new to php and sql so apologies for any amateur coding, I have taught myself php.
I have been reading for the past two weeks and trying to get my head around how to do the simplest of tasks. Maybe it's the MVC I am working with or it's just my inability to figure it out. I find myself copying chunks of code from one model/view/controller to the next in order to achieve my desired results, however this isn't ideal as I would like to know what the code does or I won't be learning.
I have been doing a lot of independent reading and following tutorials online however most of them are aimed towards CodeIgniter or CakePhp, therefore the syntax is different. I know that once I finally get my head around the syntax and logics then I won't have any issues.
I will use a simple example for reference below. Hopefully somebody will be able to shed some light on this for me and help me out.
I have been trying all day today to select the number of rows in my table and display this on a user profile page as "You have this number of favourites: x".
As I understand, I create the query in my model which is favouriteTotal(). I then reference this is the controller and render the view? Using PDO to connect to db.
login.php (model)
class LoginModel
{
public function favouriteTotal()
{
$query = $this->db->prepare("SELECT COUNT(*) FROM favourite");
$query->execute();
$count = $query->rowCount()
}
}
login.php (controller)
class Login extends Controller
{
function showProfile()
{
$login_model = $this->loadModel('Login');
$login_model->favouriteTotal();
$this->view->render('login/showprofile');
}
}
showprofile.php (view)
<div>
Your have this number of favourites: <? NUMBER OF FAVS HERE ?>
</div>
This is kind of a general answer about how it often works... Please tell us what framework you are using if you want a specific answer.
Many MVC applications use a ViewBag, a 'Container' of sorts, that gets passed along to the view that's being rendered.
(Updated the code below to reflect how the used framework passes data to the view)
login.php (controller)
class Login extends Controller
{
function showProfile()
{
$login_model = $this->loadModel('Login');
$this->view->count = $login_model->favouriteTotal();
$this->view->render('login/showprofile');
}
}
showprofile.php (view)
<div>
Your have this number of favourites: <?php echo $this->count; ?>
</div>
So, in the controller you assign the value of $login_model->favouriteTotal() to $this->view->count. Then you render the view. In that view, you call $this->count to access the previously sent data.

How do I make this code reusable on different views? Proper method

I am somewhat confused on the proper way to perform the following. Is this a class a function or an object? (Not sure) So here goes (please be kind) I'm learning codeigniter/php simultaneously.
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
echo 'Hello, '.$is_live.
' <b>(Not you? '.'<a href="' .base_url().
'main/logout">Log Out</a>) </b>';
I wrote this code, which checks if a session is set and if true echo the details. I currently have this code on my view/main However I want to have this code displayed on all pages.
What is the proper way to do this?
Do i create a view/header which is auto loaded on each page and insert this code there?
Do I create a class/public function in a helper file and load it where needed?
Do I create a class/public function in a library file and load it where needed?
Do I create a public function in a controller?
-EXTRA-
My assumption is option 1 but I'm curious what if I want to display this information in another location on a particular page? I don't want to have to copy/paste the code block because that is sloppy.
How would I set it up so that I can just call it where i need it?
e.g. $this->load->helper('check_for_sess');
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class isLive
{
public function is_live() {
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
echo 'Hello, '.$is_live.
' <b>(Not you? '.'Log Out) </b>';
}
}
On the view I tried this:
<?php
$isLive = new isLive;
$isLive->is_live();
?>
This however doesn't work and causes a ' function userdata() on a non-object error '
Can someone please explain the proper way to achieve my desired solution and the correct syntax. Thanks in advance!
-UPDATE-- Tried to create a library - still getting error.
#Created a library
class CheckSess
{
function IsLive()
{
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
{
$check_msg = 'Hello, '.$is_live.
' <b>(Not you? '.'Log Out) </b>';
}
return $check_msg;
}
}
#In the View
<?php echo $this->CheckSess->IsLive(); ?>
#in the controller
$this->load->library('CheckSess');
--UPDATE--
class CheckSess {
function IsLive()
{
$CI =& get_instance();
$is_live = $CI->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
{
return 'Hello, '.$is_live.
' <b>(Not you? '.'Log Out) </b>';
}
}
}
Set this^ is the libraries folder
the view shows <?php echo $this->CheckSess->IsLive(); ?>
and the controller shows $this->load->library('CheckSess');
please advise. Thanks again!
ERROR---
( ! ) SCREAM: Error suppression ignored for
( ! ) Fatal error: Call to a member function IsLive() on a non-object in C:\wampserver\www\test-app\application\views\homepage.php on line 56
Call Stack
Time Memory Function Location
1 0.0005 151432 {main}( ) ..\index.php:0
2 0.0014 187792 require_once( 'C:\wampserver\www\test-app\system\core\CodeIgniter.php' ) ..\index.php:202
3 0.0277 1537000 call_user_func_array ( ) ..\CodeIgniter.php:359
4 0.0277 1537048 Main->index( ) ..\CodeIgniter.php:359
5 0.0283 1540200 CI_Loader->view( ) ..\main.php:8
6 0.0283 1540640 CI_Loader->_ci_load( ) ..\Loader.php:419
7 0.0287 1566584 include( 'C:\wampserver\www\test-app\application\views\homepage.php' ) ..\Loader.php:833
The best ways to write such reusable in CodeIgniter are Models and Libraries.
With libraries being better because the code and logic you use isn't actually what models in MVC are, but with CodeIgniter and especially for a beginner it's pretty much ok to write this in models if you are not planning to distribute or share this code in any way.
I would suggest reading more on what goes where in CodeIgniter, you can find plenty information here, on SO or on official CI forums, but as a quick reference, I will write a list of what goes where.
Views
Static, dumb data output, formatting, iteration(for displaying tables, lists, etc.).
The only things you want to use here are some of the basic PHP functions, CodeIgniter helpers, for, foreach, while loops and conditionals (if, case)
Controllers
Form data capture and validation, catching data from models, sending data to views, redirects.
Models
Querying, reading, writing and updating data from databases, remote sources (APIs), files.
Helpers
Often used functions that are expected to be used in views - formatting HTML, parsing and displaying trees as lists <ul><li></li></ul>, shorthand functions for other functions like
function mydate() {
return date("d.m.Y");
}
Stuff that does not use any of the CodeIgniter libraries/models (Technically it can use, but should be avoided in most cases).
Libraries
Custom classes and objects, data processing, API interfaces, complex of the aforementioned
If you want to display some view file with data, I would put it in a library. You can put it in to a model. In my projects when I use some parts, I have a library/model (difference in our case is minimal), lets call it pmodel.
class Pmodel extends CI_Model() {
function ShowHeader() {
$data = array(
'user' => $this->usermodel->GetCurrentUser()
}
$this->load->view('header', $data);
}
function ShowMyPart() {
$is_live = $this->session->userdata('email');
$is_live = ucwords($is_live);
if (!$is_live == null)
echo 'Hello, '.$is_live.
' <b>(Not you? '.'<a href="' .base_url().
'main/logout">Log Out</a>) </b>';
}
}
Now anywhere in the view files you can call $this->pmodel->ShowMyPart() if you have loaded it before (models of this type should be autoloaded anyway).
There is one little change I would do as it's considered as good practice - never echo anything from anywhere but the view files, return values instead.
Change the echo keyword to return and in your view file simply write echo $this->pmodel->ShowMyPart().
Why should you do that? Because, if you would like to call any method that echoes something from the controller right between loading header and footer(eg.), CodeIgniter will echo this before the header as CodeIgniter does not echo your loaded views right away, it saves them to output storage and sends the finalized output right before destructing himself. More reading can be found here.
But you can do even better - use a view file to output any data you want, just create a helper and load it from your model/library as you do in your controllers.
Hope that helps!
EDIT
For your edited question, the reason why you are getting errors with your library is that your library doesn't know that an instance of CodeIgniter even exists as you are not extending your library from it, as you do with your models and controllers. You don't use $this to refer to CodeIgniter classes in your libraries, as $this is a reference to your library itself, which is a separate class. To use CodeIgniter classes you should get a CodeIgniter instance:
$CI =& get_instance();
And then you can use $CI instead of $this to call CodeIgniter classes by changing the line:
$is_live = $this->session->userdata('email');
to
$is_live = $CI->session->userdata('email');
You could make a basic html helper and autoload it in the config file.
Then you can make a function for this...
function displayLoginStatus($em)
{
$html = '';
if($em)
{
$html = 'Hello, '.ucwords($em).' <b>(Not you? '.'Log Out) </b>';
}
return $html;
}
then you call it like a normal function:
<?php echo displayLoginStatus($this->session->userdata('email')); ?>
but ideally you would place something like this in a template or header file that wraps your standard page views. There's no need to make a function out of something like this since typically you don't have the login / logout in multiple places on the page.
It could be condensed simply to:
<?php if($this->session->userdata('email')): ?>
Hello, <?php echo ucwords($this->session->userdata('email')); ?> <b>(Not you? Log Out) </b>
<?php endif; ?>
I prefer to make a site template and wrap the views with it. Makes common elements much easier to handle.
Some people template it this way:
http://codeigniter.com/forums/viewthread/88335/
There are many ways you could handle this, but the general idea is to separate your most common elements into one template view. I don't know of a succinct way of instructing you on how to do this so I will only update this post if you feel the need for much more explanation on templating.
helper docs:
http://codeigniter.com/user_guide/general/helpers.html

How to "globalize" PHP variables?

I have a page named ChangeApprovalInfo.php - It has a function called Row_Rendered as follows;
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$RecordOwner = $this->RequestUser->CurrentValue;
echo $RecordOwner;
}
Echoing $RecordOwner gets me the data I will need for a sql query on another page....
I have another page called ChangeApprovalEdit.php - This page has
<?php include_once "ChangeApprovalinfo.php" ?>
at the top of the file.
ChangeApprovalEdit.php has a function where I need the $RecordOwner variable as defined in ChangedApprovalInfo.php
If I add "echo $RecordOwner" on the ChangeApprovalEdit.php page, I get an error saying it's an unknown variable. My understanding is that I need to "make it global" or some such business. I know very little about PHP and the pages I am editing are long and complex. (to me, at least)
What do I need to do? I know that the information I have provided might not be enough to answer the question. I don't know enough to even know exactly what I need to ask. If more information is needed, I will edit and follow up.
pastebin of the files
ChangeApprovalInfo.php = http://pastebin.com/bSRM1wwN
ChangeApprovalEdit.php = http://pastebin.com/AStG9pqb
EDIT:
Changing Row_Rendered to this seems to be more effective. I'm having trouble seeing WHERE I can later echo this variable... but I'm getting somewhere with this...
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
}
Don't echo variables from functions, which just outputs them to the standard output. return them from the function so you can use the value elsewhere as well.
function Row_Rendered() {
$RecordOwner = $this->RequestUser->CurrentValue;
return $RecordOwner;
}
Then instead of
$obj->Row_Rendered();
use
echo $obj->Row_Rendered();
and if you want to use the value elsewhere, use
$value = $obj->Row_Rendered();
You can do a couple of things:
First, you can return $RecordOwner from the function, and store its value in a variable. This method is usually preferred.
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$RecordOwner = $this->RequestUser->CurrentValue;
echo $RecordOwner;
return $RecordOwner;
}
// Store it in a variable when calling the function.
$RecordOwner = Row_Rendered();
Or, you can make it global inside the function:
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
echo $GLOBALS['RecordOwner'];
}
You can use the $GLOBALS superglobals array, like this:
function Row_Rendered() {
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
}
However, you should not do that. Instead, refactor your application so that the view in ChangeApprovalinfo.php just contains a function, which is then called with the appropriate parameters.
EDIT: Chaning Row_Rendered to this seems to be more effective. I'm having trouble seeing WHERE I can later echo this variable... but I'm getting somewhere with this...
function Row_Rendered() {
// To view properties of field class, use:
//var_dump($this-><FieldName>);
$GLOBALS['RecordOwner'] = $this->RequestUser->CurrentValue;
}
I feel compelled to write another answer to this update. Let me demonstrate the use of globals as seen from outside that function:
$obj->Row_Rendered();
$obj->foobar();
echo $GLOBALS['RecordOwner'];
Quick, what will be echoed and where does that value come from? Well, it depends on what $obj-foobar() does. Maybe it changes the global variable. Maybe it doesn't. Who knows if the variable has been set at all? How would you trace back what happened exactly without adding a debug line after every single function call?
And that's just three lines of code. Imagine that in an application of any complexity.
Now, the same thing if I return the value from Row_Rendered:
$owner = $obj->Row_Rendered();
$obj->foobar();
echo $owner;
If the Row_Rendered method is behaving as it should (returning the owner), this code is very predictable. If you do not follow this pattern, you'll have a hell of a time getting anything done when the application grows to any halfway complex size.
Set the variable as global from within the function
$my_global_var = "old value";
function doing_stuff(){
global $my_global_var; //this will use the global variable instead of creating a local one
$my_global_var = "new value";
}
echo $my_global_var;//returns "new value"

Drupal - How can I make an array globally accessible?

I'm using this code in a views field template (in this case views-view-field--all-members--uid.tpl.php):
<?php
$users_friends = flag_friend_get_friends($user->uid);
$users_friends_ids = array();
foreach ($users_friends as $id => $value) {
$users_friends_ids[] = $id;
}
?>
It basically gets the user ids of friends and puts them in an array so I can check if the field matches any of the user ids.
So my problem is that I don't want to have this within this template (for a few reasons), but if I don't I can't access the array. How can I make this array globally accessible?
Without knowing your "few reasons", I can't say if this is the answer for sure. My own reasons would probably be that I don't want the same code executing a bunch of times, and I'd rather not have the same exact code in multiple places.
I would then create a function with a static variable to hold the friends array.
function mymodule_get_friends_ids() {
// pull in the current global user variable
global $user;
// call up the static variable
static $users_friends_ids;
// return if this static var has already been set
if (is_array($users_friends_ids)) {
return $users_friends_ids;
}
// if we hit here, then this function has not been
// run yet for this page load.
// init array
$users_friends_ids = array();
// if user is anon, no need to go on
if (user_is_anonymous()) {
return $users_friends_ids;
}
// get friends array
$users_friends = flag_friend_get_friends($user->uid);
// build ids array
foreach ($users_friends as $id => $value) {
$users_friends_ids[] = $id;
}
return $users_friends_ids;
}
Now in your templates, you can call mymodule_get_friends_ids() in as many places as you want, and the working code below the first return will only get executed the first time it is called.
Coder1's advice is very good - it keeps you from populating your global variable namespace with a lot of junk. It's probably the most "elegant." It might not be the easiest to use if you are rather new to PHP (which I'm guessing might be the case if it's hard to get your head around returning arrays, but that's ok).
However, if this is really a priority, you probably don't care about having one extra global variable.
I suppose I may be stating the obvious here - but you can, at pretty much any point in execution (provided the information you need has already been generated - e.g., the $user variable has been populated), do this:
$GLOBALS['users_friends_ids'] = /* your code goes here */
Then in your template, you access this by ...
$friendsArray = $GLOBALS['users_friends_ids'];
Or you can simply use the construct
global $user_friends_ids;
when you want to initialize the variable, or access it inside a function or class (which is the case for your template files - they are called inside functions, so you need to globalize or use the $GLOBALS array, which is "automagically" all of the variables active in the global namespace).
The most "logical" place to do this would be inside a module using one of the many hooks available, to execute this code only once. hook_init() might do it for you, if the user object is already loaded at this point (not sure, you'll have to test). But you might not want to figure out making Drupal modules (it's not that difficult).
If you are doing this inside a template (and though it's not good practice, many Drupal site owners with a beginning knowledge of PHP put everything in templates), you'll want to know which template code is being executed when. Node template code tends to be executed before page template code - which is logical, since otherwise the variables for node content in the page template wouldn't be populated.
If you have listings of nodes, they'll be calling this code multiple times, so you'll end up doing something similar to what Coder1 is describing. If you don't want to create your own small module, you could put the function declaration he's written in your theme's template.php file, since it's called only once. You don't want to put function declarations in the tpl.php files, since they are sometimes called more than once (and you aren't allowed to declare functions more than once).
If you have a hard time understanding the function and the return, you can always do something like this in your code (which is very, very inelegant - but it's better to have inelegant code that you do understand, than elegant code that's you don't).
if(!isset($GLOBALS['users_friends_ids'])) {
$GLOBALS['users_friends_ids'] = /* your code here */
}

Categories