sonicsleuth / mvc-framework-php

A Model-View-Controller framework written in PHP. Easy Install. Auto/Pattern-Routing, Schema Mapping Models using PDO.
MIT License
3 stars 1 forks source link

Found a pretty big bug!!! #6

Closed jackttcoms closed 6 years ago

jackttcoms commented 6 years ago

Ok! I have created a simple blog and im using a foreach to get the data but when there is only one result in the db i get a tonne of odd errors!

The bit of code that is causing this error:

        if(empty($data)) { 
            return false; // return FALSE
        } elseif (count($data) > 1) {
            return $data; // return full index of records.
        } else {
            return $data[0]; // return single record.
        }

I tried changing it to this but unfortunately this then broke the login:

        if(empty($data)) { 
            return false; // return FALSE
        } elseif (count($data) => 1) {
            return $data; // return full index of records.
        } else {
            return $data[0]; // return single record.
        }

Here is my code, maybe i have done something wrong:

                        <?php foreach($data as $post) : ?>

                            <div class="col-lg-6 col-md-6 padding15">
                                <div class="card shadow-2 mb-3"><a href="<?php echo FULL_ROOT; ?>/blog/post/<?php echo $post['id']; ?>"><img class="card-img-top w-100 d-block" src="<?php echo FULL_ROOT; ?>/uploads/blog/<?php echo $post['image']; ?>"></a>
                                    <div class="card-body pb-0">
                                        <ul class="list-inline mb-1">
                                            <li class="list-inline-item"><a href="<?php echo FULL_ROOT; ?>/blog/category/<?php echo $post['cat_id']; ?>">Blog Tag 1</a></li>
                                        </ul>
                                        <h5 class="card-title font-weight-bold"><a href="<?php echo FULL_ROOT; ?>/blog/post/<?php echo $post['id']; ?>"><?php echo $post['title']; ?></a></h5>
                                    </div>
                                    <div class="card-header bg-white">
                                        <p><?php echo $post['description']; ?></p>
                                        <div class="clearflix">
                                            <ul class="list-inline mb-0">
                                                <li class="list-inline-item"><i class="far fa-clock"></i> <?php echo helper_format_date($post['published']); ?></li>
                                                <li class="list-inline-item"><i class="fas fa-user"></i> By Admin</li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <?php endforeach; ?>

Here is my controller code:

    public function index()
    {
        $data = $this->blog->getPosts();
        $this->view('blog/index', $data);
    }

Here is my model code:

    public function getPosts()
    {
        $results = $this->db->select('msi_posts','status = 1');
        return $results;
    }

Here is the DB when only 1 post is active: image

Thanks and if you are on holiday still you do not have to answer!

sonicsleuth commented 6 years ago

The function you refer to as "buggy" is not. It is designed to return an associative array if more than one record is found and a sequential array for a single record because it is used for both multiple and single queries, this is typical.

In your getPosts() method, you should check the value-type of the first element found in the returned records. If the value is an array return the results as is. If it is NOT an array, array_push() the results into a new array (making it become an associative array of one record) and return this new array. Then your foreach loop will work fine.

Read up on is_array() here http://php.net/manual/en/function.is-array.php

jackttcoms commented 6 years ago

i'm really sorry but i don't fully understand what you mean.

Do you mean something like this?

function is_assoc($test) {
        if (empty($test)) { return(false); }
        if (!is_array($test)) { return(false); }
        return ($test !== array_values($test));
}

or

if ( (array) $unknown !== $unknown ) {
    echo '$unknown is not an array';
} else {
    echo '$unknown is an array';
} 

or

foreach($something as $value){
    if(!in_array($value, $liste, true)){
        array_push($liste, $value);
    }
}

Thanks in advance!


AND REGARDS TO PREVIOUS ISSUE...

That's great thanks so much and i know this is only minor but is it possible to remove the gaps in between? image

Also i have one final major question and its regarding MVC's in general! As i'm relatively new to them i'm wondering how i get data constantly from the database or how would i do it because basically in my db i have a table called 'settings' and it has 'title || Brand Name' in it so how would i get that title echoed on every page?

Do i do a helper or how do i get the entire settings database on hand ready for whenever i need it?

In my old code i used to do it like this - <title><?php echo $settings['title']; ?> - MVC Framework</title>

Thanks again

-- Also here is a gif of what i have made so far using your brilliant framework :) https://gyazo.com/c24c527f1529a834c96e1ff682d20225

sonicsleuth commented 6 years ago

To remove the empty line after each stylesheet line experiment with the trailing string that forces a return after each line. The "\r\n" is code for "return" and "newline". Try removing just the escaped \n and keep the \r. The two are used b/c of page encoding - if one failed the other would work. In your case, it seems both are being applied - one too many returns.

function load_style($paths)
{
    foreach ($paths as $path) {
        echo '<link rel="stylesheet" href="' . WEB_ROOT . 'css/' . $path . '.css" type="text/css" />' . "\r\n";
    }
}

To check if you have an associative array - Use this technique in your Controller to test the record results you get back from your Model query.

if(!is_array($results[0])) {
            // One Record.
            $new_results = array();
            array_push($new_results, $results);
            print_r($new_results);
        } else {
            // Multiple Records.
            return $results;
        }

If one record is returned, it will change the results from:

Array (
   [id] => 1
   ...
) 

into this...

Array(
   [0] => Array (
                  [id] => 1
                  ...
               ) 
)

and then your foreach will work fine.

sonicsleuth commented 6 years ago

As for having access to "settings" each time the page loads, making a "Helper" function is one way to go. Just have the Helper Settings function you make contain anything you comonly need.

Another approach, if the common values are gathered after login, would be to store those values in a session after login. Then you avoid the DB lookup on every page.

If you are referencing "static" data, like the name of your product for use in an HTML tag as a "configuration" - just append your items to the MVC config file here /app/config/config.php like so...</p> <p><code>$config['site_name'] = 'My Great Site';</code></p> <p>Then use it like...</p> <p><code>echo $config['site_name'];</code></p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Thanks that worked great :) There is one error that appears but of course i can just hide it by changing the development mode but here it is: <code>Notice: Undefined offset: 1 in /customers/a/d/5/ttcoms.com/httpd.www/app/models/Blogs.php on line 22</code></p> <hr /> <p>Also as mine is a php flexible script i wanted to be able to edit the variables from the actual admin panel which is connected to the DB like here in my old script: <img src="https://user-images.githubusercontent.com/26184209/40791448-6b03ad14-64ef-11e8-8547-a64234c15e29.png" alt="image" /></p> <p>but i just don't know how to do it effectively and with helpers i cant do public functions can i ?</p> <p>This is how i used to do it: $settings = new Settings($DB_con); // Fetch Settings $setting = $settings->get_all();</p> <p>and then the file was: </p> <pre><code><?php class Settings { var $error = false; var $msg = false; private $db; function __construct($DB_con) { $this->db = $DB_con; } public function get_all() { $setting = array(); $result = $this->db->prepare("SELECT * FROM " . PFX . "settings"); $result->execute(); while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $setting[$row['setting']] = $row['value']; } return $setting; } public function update($newsettings) { foreach ($newsettings as $key => $value) { $update = $this->db->prepare("UPDATE " . PFX . "settings SET `value` = :value WHERE setting = :key"); $update->bindParam(':value', $value); $update->bindParam(':key', $key); $update->execute(); if (!$update) { $this->error = "Error saving settings"; return false; } } if (empty($this->error)) { $this->msg = "Settings updated successfully"; return true; } } } ?></code></pre> <p>any way i can do this just much more optimised?</p> <p>Thanks again!</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Not sure what you mean here. It looks like your Settings class example as shown could be done as a Model class called Settings, then add a Controller for get_all and/or update in my MVC pretty easily.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>wait so please can you expand on what you mean by controller for get_all because i really am over complicating it but i want to get my mvc right the first time and not do anything that is damaging to speed etc. </p> <p>Let me see if i got this right:</p> <ul> <li>I create a model called settings.php (done)</li> <li>I then do a get_all function (done)</li> </ul> <p>After the above i get wary as to put things... Then in the public construct() do i do something like this</p> <pre><code> public function __construct() { $this->load_helper(['view']); $this->setting = $this->model('Settings'); // Instantiate a new user object. }</code></pre> <p>but then after that i get confused, do i then call the settings get all function in every controller function/page? or is there a way to do globally and then regarding doing multiple models in one controller function... like this:</p> <pre><code> public function users() { // Get data from model $data = $this->user->getUsers(); ////I cant do this surely $data = $this->setting->getAll(); // Load view $this->view('admin/users', $data); }</code></pre> <p>surely if i wanted to do the settings as well i would have to put the $data into an array or something because when im echoing the <code>$data['some setting table data'];</code>it would also look at the <code>$data['some user table data'];</code>please can you explain and then after this i feel like i can go on my own ans stop bothering you with my questions and i will also send you my simple working blog which you can add here as a demo.</p> <p>Thanks again so much</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Generally, you are going in the right direction...</p> <p>Think of your Model Classes as the place where all forms of data are gathered, including results from a database or perhaps the dynamic build of a PDF doc you will offer the user for download, like a billing statement. When you think of "data" you make a Model Class.</p> <p>Think of Controllers as the "logic center" that determines what should be sent to the View. A Controller will call upon 1+ Models to "collect all values" and merge them into the <code>$data</code> array.</p> <p>When passing data from more than one Model, in your case Users and Settings, you CAN make the $data array associative to hold each Model results like so, and to do this just assign the results from a Model using a variable named, like <code>$user_data = $this->user->getUser('123');</code></p> <p>Then add each of those arrays (or strings) to the primary <code>$data</code> array that you will pass to the View. This pattern is similar to say a big JSON object with nested nodes.</p> <pre><code>$data [ 'user_data' => array('id' => 1, 'name' => 'john smith', ...), 'settings' => array('login' => 'yes', 'last_login' => '2018-06-01') ]</code></pre> <p>Then in the View access each value like so:</p> <p><code>$data['user_data']['name']</code></p> <p>There are 3 patterns where to place the creation of common data requirements in a Controller, they are: (let's use your Settings for an example)</p> <ol> <li>If the Settings are needed by the majority of methods in the Class, instantiate Settings in the __constructor as this saves time.</li> <li>If only a few methods require Settings, then you COULD instantiate Settings in only those methods (locally) which will remove any overhead for methods NOT requiring Settings.</li> <li>If Settings needs to persist across pages, such as after a login you want to reuse the Profile Image, then you should query your Settings once in your Authentication (or Login) Controller and store those values into a PHP session. Then in the View call the Session values as needed. This removed the constant database queries for data that generally does not change between page views. (Of course, if the user updates their Settings you must update the PHP Sessions too).</li> </ol> <p>Here is a crude diagram of how to think about MVC...</p> <pre><code>Model A I can get data A1 I can get data A2 Model B I can get data B1 I can get data B2 Controller __constructor { I need data from A1 I need data from A2 I need data from B1 } $data = [A1, A2, B2] $this->view(..., $data)</code></pre> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>thanks very much for the array help and just so i can double check i have done it correctly:</p> <p>View Code:</p> <pre><code><?php echo $data['settings']['global_company']; ?> <div class="container"> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th scope="col">#</th> <th scope="col">Username</th> <th scope="col">Role</th> <th scope="col">Status</th> <th scope="col">Actions</th> </tr> </thead> <tbody> <?php foreach($data['users'] as $user) : ?> <tr> <th scope="row"><?php echo $user['id']; ?></th> <td><?php echo $user['username']; ?></td> <td><?php echo $user['role']; ?></td> <td><?php if ($user['status'] == 1){ ?><span class="badge badge-success">Active</span><?php } elseif($user['status'] == 2){ ?><span class="badge badge-danger">Banned</span><?php } else{ ?><span class="badge badge-warning">Pending</span><?php } ?></td> <td><a href="<?php echo FULL_ROOT; ?>/admin/users/edit/<?php echo $user['id']; ?>" type="button" class="btn btn-primary btn-sm">Edit</a><a href="<?php echo FULL_ROOT; ?>/admin/users/view/<?php echo $user['id']; ?>" type="button" class="btn btn-primary btn-sm">View</a></td> </tr> <?php endforeach; ?> </tbody> </table> </div> </div></code></pre> <p>Controller Code:</p> <pre><code> public function users() { // Get data from model $udata = $this->user->getUsers(); $sdata = $this->setting->getAll(); $data = array( "users" => $udata, "settings" => $sdata ); // $data = $this->setting->getAll(); // Load view $this->view('admin/users', $data); }</code></pre> <p>but regarding speed etc is there a way to call it like this? <code><?php echo '<img src="' . SITEURL . '/uploads/' . App::Core()->logo . '" alt="'.App::Core()->company . '">'; ?></code> The app is the main bit that does the application like this $app = new App($config, $route); but then it calls the core class which is public functions and then you call the name but here it looks like you only call it when you need it and by that i mean you only load the specific data you need whereas the method above loads all the data, so with your MVC is it possible to so something like the above?</p> <p>Thanks again and regarding sessions would you recommend i use a session class and if so do you have any samples or should i just simply use these settings: <code>$_SESSION['userpic']</code></p> <p><?php</p> <pre><code>$l = array ( "search"=>"Search", "login"=>"Login" );</code></pre> <p>?> Also im trying to do a language file which works like this: <?php echo $l['search']?> then it goes to the array and gets the data but im stuck in knowing where i put the require_once 'lang/en.php'; I have tried it in the init.php file config.php file and public/index.php file but none seem to work! What am i doing wrong?</p> <p>And regarding this bit of code, what should i do to fix this so that this error no longer appears... <code>Notice: Undefined offset: 0 in /customers/a/d/5/ttcoms.com/httpd.www/app/models/Users.php on line 63</code></p> <pre><code> if(!is_array($results[0])) { $new_results = array(); array_push($new_results, $results); return $new_results; } else { return $results; }</code></pre> <p>Thanks again so very much!</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Your use of the MVC as I described it to you is good.</p> <p>Calling a Model "statically" from the View breaks away from the MVC pattern and is generally discouraged. If you go down that road eventually your project will not scale well. You also want to pass in the data to a view b/c you may want to adjust it before use. You would not want to implement filters in the View. The controller is a central hub for data modification before the presentation.</p> <p>$_SESSIONS[] is an overall good solution.</p> <p>I would not worry about the "Undefined Offset" as it is only a PHP "notice" NOT an error. It will not STOP the code from executing. Just make sure to configure your PHP.ini file to hide all Notices. (ie: don't let PHP complain about nothingness)</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Thanks very much! My blog is almost done so i will send it to you later and you can check it etc and if up to your standard you can put it on your Github as a sample project :)</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Hi, how would you suggest i do this with your MVC? Thanks</p> <p>Basically with users, products etc when there are none in the database on the view bit how do i do a message like 'there are no products, why not add one?' etc. At the moment it just shows an empty placeholder like this: <img src="https://user-images.githubusercontent.com/26184209/40966138-8cbdf1ba-68a7-11e8-9435-ae8fa976ef97.png" alt="image" /></p> <p>Here is my code: Model Code:</p> <pre><code> public function getPosts() { $results = $this->db->select('msi_posts','status = 1'); if(!is_array($results[0])) { $new_results = array(); array_push($new_results, $results); return $new_results; } else { return $results; } }</code></pre> <p>View COde:</p> <pre><code> <?php foreach($data['post'] as $post) : ?> <div class="col-lg-6 col-md-6 padding15"> <div class="card shadow-2 mb-3"><a href="<?php echo FULL_ROOT; ?>/blog/post/<?php echo $post['slug']; ?>"><img class="card-img-top w-100 d-block" src="<?php echo FULL_ROOT; ?>/uploads/blog/<?php echo $post['image']; ?>"></a> <div class="card-body pb-0"> <ul class="list-inline mb-1"> <li class="list-inline-item"><a href="<?php echo FULL_ROOT; ?>/blog/category/<?php echo $post['cat_id']; ?>">Blog Tag 1</a></li> </ul> <h5 class="card-title font-weight-bold"><a href="<?php echo FULL_ROOT; ?>/blog/post/<?php echo $post['id']; ?>"><?php echo $post['title']; ?></a></h5> </div> <div class="card-header bg-white"> <p><?php echo $post['description']; ?></p> <div class="clearflix"> <ul class="list-inline mb-0"> <li class="list-inline-item"><i class="far fa-clock"></i> <?php echo helper_format_date($post['published']); ?></li> <li class="list-inline-item"><i class="fas fa-user"></i> By Admin</li> </ul> </div> </div> </div> </div> <?php endforeach; ?></code></pre> <p>Controller code:</p> <pre><code> public function index() { $pdata = $this->blog->getPosts(); $sdata = $this->setting->getAll(); $data = array( "post" => $pdata, "settings" => $sdata ); if($pdata == false) { $this->view('404'); die(); } $this->view('blog/index', $data); }</code></pre> <p>Thanks</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>The common pattern is to check for any records within the Controller and assign a true/false value to a variable you will also pass into the <code>$data</code> array... something like <code>$data["isRecords", ..., ...]</code> and then check for this value in the View like this:</p> <pre><code><php if(isRecords) { ?> ... foreach records html ... <?php } else { ?> ... other message ... <?php } ?></code></pre> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Sorry but I don't fully understand by the assign a "true/false value to a variable". Please can you expand on what you mean.</p> <p>This is my controller: public function index() { $ifdata = $this->item->getFreeItems(); $ifedata = $this->item->getFeaturedItems(); $sldata = $this->slide->getFeaturedSlides(); $sdata = $this->setting->getAll(); $data = array( "itemfree" => $ifdata, "itemfeat" => $ifedata, "slide" => $sldata, "settings" => $sdata ); $this->view('home', $data); } please can you expand on what you meant. thanks again</p> <p>The view:</p> <pre><code> <?php if(isRecords) { foreach($data['itemfree'] as $item) : ?> <div class="col-lg-4 col-md-4 col-sm-6 mb-3"> <div class="card h-100 shadow-1"> <a href="<?php echo FULL_ROOT;?>/item/<?php echo $item['id']; ?>/"> <img class="card-img-top" src="<?php echo FULL_ROOT;?>/uploads/items/<?php echo $item['id']; ?>/<?php echo $item['preview_img']; ?>" alt="<?php echo $item['name']; ?>"> </a> <div class="card-body text-center"> <a href="<?php echo FULL_ROOT;?>/item/<?php echo $item['id']; ?>/"><h2 class="card-title font-weight-bold f-18"><?php echo $item['name']; ?></h2></a> <div class="clearfix"> <a data-toggle="tooltip" data-placement="top" title="Like" href="#" class="btn btn-sm btn-light btn-lightb float-left mr-1"><i class="far fa-heart"></i></a> <a data-toggle="tooltip" data-placement="top" title="Live Preview" href="<?php echo $item['demo']; ?>" target="_blank" class="btn btn-sm btn-light btn-lightb float-left"><i class="fas fa-desktop"></i></a> </div> </div> <div class="card-footer bg-white"> <div class="clearfix"> <button type="button" class="btn btn-sm btn-light float-right btn-lightb"><?php echo $data['settings']['payment_currency_sym'].$item['price']; ?></button> </div> </div> </div> </div> <?php endforeach; } else { ?> ... other message ... <?php } ?></code></pre> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Also why is it that i'm able to get to echo this piece of code: <code>define('FULL_ROOT', 'https://domain.com');</code> from the public/index.php like this <code><?php echo FULL_ROOT;?></code> but when i do this <code>$full_root = 'https://domain.com';</code> i get an undefined variable error. <code>Notice: Undefined variable:</code></p> <p>Do you know why?</p> <p>Thanks</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>I would be best if you open new issue tickets for problems/questions specific to this repository code. There are plenty of resources online that can teach you how to program in PHP. </p> <p>That said, let's clarify the last answer I gave you last...</p> <p><strong>CONTROLLER</strong> After you get each of your record collections within your <code>index()</code> method like <code>$freeItems = $this->item->getFreeItems();</code> and so on, you want to see if <code>$freeItems</code> and the other record sets have any records by checking if the array for each has any data. A basic example is:</p> <pre><code>public function index() { $freeItems = $this->item->getFreeItems(); if(is_array($freeItems[0]) ) { $isFreeItems = true; // $isFreeItems will be true or false depending on if there are records. } else { $isFreeItems = false; } ... $data = [ "freeItems" = $freeItems, "isFreeItems" = $isFreeItems, .... ] $this->view('home', $data); }</code></pre> <p><strong>VIEW:</strong></p> <pre><code><?php if( **$isFreeItems** ) { foreach($data['itemfree'] as $item) : ?> ... your list of items ... <?php endforeach; } else { ?> ... other html message you want to display ... <?php } ?></code></pre> <p><strong>Another basic programming question being asked here...</strong></p> <p>Your question is about variable SCOPE - The <code>define('FULL_ROOT', 'https://domain.com');</code> is gloablly defined whereas <code>$full_root = 'https://domain.com';</code> is locally defined (from what I can tell from your remarks). </p> <p>If $full_root is being used once, then define it in your Controller method and pass it as another array index value of <code>$data</code>. If you need it everywhere, then define it in <code>/app/config/config.php</code> for Global Scope.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Of course I will do that in the future and I just want to apologise to you for asking the silly questions.</p> <p>I do know PHP but when I used to code in PHP I did it in a way where I could access anything at anytime and I wrote in pure php with pure php pages so not in the MVC structure etc. Basically it just overwhelmes me as it’s completely different to what I am used to where I could call anything very very easily.</p> <p>Thanks for clarifying with me the define vs variable. I never knew that officially regarding the define is globally defined as I always used variables on a config.php file and every file included it etc.</p> <p>Sorry again! I’m literally getting the hang of this and will stop bothering you which I hate doing and what i will say is that my array knowldge needs a refresh as i always used to use SQL queries to count and then i would do a $num >0 { sort of thing.</p> <p>I will send you the blog demo tomorrow if you want and you can use it as a demo if you think it’s all correct etc and then if ppl decide to use your framework they will have a sample to look on and they will not ask you so many questions 😁 like I am doing. </p> <p>Thanks again and sorry again!</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>If this helps you to understand the MVC pattern. </p> <p>The MVC pattern helps to separate core responsibilities of an application. This way you can "focus" on just the purpose of the M or V or C. </p> <p>The Controller is the Boss. HTTP requests call defined URL "endpoints" for information. The Controller might know the answer (doing some calculation) but most typically will "ask" a data Model for answers. The Controller could seek answers from 1+ Models as you have in your project. That's all good... the Controller (Boss) is "delegating" to Model (Workers). </p> <p>Models are "workers" that handle getting data from a "data source", by this we mean, a database, a remote API to a third-party service that needs parsing first, pulling down a PDF from some remote repository, etc. As you can see "data" (aka Models) can come from many sources.</p> <p>So why might it be good for a Controller to call a Model for some database records vs doing it in the Controller directly? Imagine if you have a single function in your Model for getting "all users". Your Controller might have two functions: one that returns a View listing these Users, and another that returns the User data as a JSON response for some other purpose. We now have one data source with two "formatted" results to meet your needs within the Controller.</p> <p>Views are technically just "templating" of data within HTML. Much like how you probably wrote PHP to date. The only difference is that we carefully control what data is given to the View so that we minimize layering in PHP throughout the HTML. Mostly string output, loops, if/then is all you should need with PHP.</p> <p>Think about this... Oh no, I see there is a bug with my User list on this page. It looks to be a data error so I know that I can look for the User Model to fix it. (You can debug faster) ... OR ... My partner vendor needs the data as XML. I can just add a new Controller method to format the same Model data as XML and give them a new specific URL endpoint. (You can roll out new services faster).</p> <p>Of course, there are Helpers which are small distinct functions/features you need that just don't fit into anything. Routes are also powerful because they help clarify the purpose of each URL endpoint.</p> <p>I hope this helps, enjoy.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/jackttcoms"><img src="https://avatars.githubusercontent.com/u/26184209?v=4" />jackttcoms</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>Thanks, that clarified a lot regarding MVC's! Thanks for being so great at support i am slowly but steadily getting there. Arrays I guess are my biggest weakness so i will re-read etc and learn how to use them properly again.</p> <p>Also with MVC's above you mentioned:</p> <blockquote> <p>(You can roll out new services faster).</p> </blockquote> <p>So with an MVC does this mean its possible to do a global universal pagination so one that works for everything? Below is one for my experimental bits of code when i first tried the MVC style but let me know if you want me to create a new ticket for this:</p> <p>Anyway thanks again very very much!</p> <p>My Model Code:</p> <pre><code> public function getOrdersByPagination($offset, $limiter){ $this->db->query("SELECT * FROM uk_orders ORDER BY order_date DESC LIMIT :offset, :limiter"); // Bind values $this->db->bind(":offset", $offset); $this->db->bind(":limiter", $limiter); $results = $this->db->resultSet(); return $results; } public function getOrdersByIdPagination($offset, $limiter, $userId){ $customerId = $this->getCustomerId($userId); $this->db->query("SELECT * FROM uk_orders WHERE customer_id = :customer_id ORDER BY order_date DESC LIMIT :offset, :limiter"); // Bind values $this->db->bind(":offset", $offset); $this->db->bind(":limiter", $limiter); $this->db->bind(":customer_id", $customerId); $results = $this->db->resultSet(); return $results; }</code></pre> <p>Controller:</p> <pre><code>public function page($page=1){ // Check if there is logged-in user if(isset($_SESSION['user_id'])){ // Page number $page = $page; // Number of items per page $limit = 5; $adjacents = 2; // Get total rows // Check if admin if(isset($_SESSION['admin_mode'])){ $this->orderModel->getOrders(); } else { $this->orderModel->getOrdersById(intval($_SESSION['user_id'])); } $totalRows = $this->orderModel->getRowCount(); // Compute total pages rounded up $totalPages = ceil($totalRows / $limit); // Compute for the offset $offset = $limit * ($page-1); if($totalPages <= (1+($adjacents * 2))) { $start = 1; $end = $totalPages; } else { if(($page - $adjacents) > 1) { if(($page + $adjacents) < $totalPages) { $start = ($page - $adjacents); $end = ($page + $adjacents); } else { $start = ($totalPages - (1+($adjacents*2))); $end = $totalPages; } } else { $start = 1; $end = (1+($adjacents * 2)); } } // Get orders of customer // Check if admin using sessions if(isset($_SESSION['admin_mode'])){ $orders = $this->orderModel->getOrdersByPagination($offset, $limit); } else { $orders = $this->orderModel->getOrdersByIdPagination($offset, $limit, $_SESSION['user_id']); } $data = [ 'orders' => $orders, 'page' => $page, 'start' => $start, 'end' => $end, 'totalPages' => $totalPages ]; $this->view('orders/index', $data); } else { redirect(''); } }</code></pre> <p>View:</p> <pre><code> <?php if ($data['totalPages'] > 1): ?> <ul class="pagination justify-content-center"> <!-- bs 4 first one --> <li class='page-item <?php ($data['page'] <= 1 ? print 'disabled' : '') ?>'> <a class='page-link' href='<?php echo FULL_ROOT ?>/orders/page/1'>&laquo;</a> </li> <!-- Link of the previous page --> <li class='page-item <?php ($data['page'] <= 1 ? print 'disabled' : '') ?>'> <a class='page-link' href='<?php echo FULL_ROOT ?>/orders/page/<?php ($data['page'] > 1 ? print($data['page'] - 1) : print 1) ?>'>&lsaquo;</a> </li> <!-- Links of the pages with page number --> <?php for ($i = $data['start']; $i <= $data['end']; $i++): ?> <li class='page-item <?php ($i == $data['page'] ? print 'active' : '')?>'> <a class='page-link' href='<?php echo FULL_ROOT ?>/orders/page/<?php echo $i; ?>'><?php echo $i; ?></a> </li> <?php endfor; ?> <!-- Link of the next page --> <li class='page-item <?php ($data['page'] >= $data['totalPages'] ? print 'disabled' : '')?>'> <a class='page-link' href='<?php echo FULL_ROOT ?>/orders/page/<?php ($data['page'] < $data['totalPages'] ? print($data['page'] + 1) : print $data['totalPages'])?>'>&rsaquo;</a> </li> <!-- Link of the last page --> <li class='page-item <?php ($data['page'] >= $data['totalPages'] ? print 'disabled' : '')?>'> <a class='page-link' href='<?php echo FULL_ROOT ?>/orders/page/<?php echo $data['totalPages'] ?>'>&raquo; </a> </li> </ul> <?php endif;?></code></pre> <p>Thanks again.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/sonicsleuth"><img src="https://avatars.githubusercontent.com/u/2731539?v=4" />sonicsleuth</a> commented <strong> 6 years ago</strong> </div> <div class="markdown-body"> <p>I understand your question as such... You need pagination for several large lists, say, Users, Products, etc... BUT you don't want to have to write the "pagination logic" repeatedly in each function as shown in your Page Controller (above).</p> <p>I would make a Helper function here <code>/app/helpers/...</code> that would isolate the Pagination computation you have in your Page Controller. Continue to request the data from your Model as you are now then pass into your Helper the value of <code>$totalRows</code> ... do all your "pagination computations" in the Helper which should return an array like [1, 5, 25] and use the PHP list() method to quickly assign each value to a variable. Here is a rough example:</p> <pre><code>... $totalRows = $this->orderModel->getRowCount(); list($page, $start, $end) = $this->PaginationHelper($totalRows); ... $data = [ 'orders' => $orders, 'page' => $page, 'start' => $start, 'end' => $end, 'totalPages' => $totalPages ]; ...</code></pre> <p>Aside... It's good practice to break out specific blocks of activities into their own functions which then return some value(s), put them in the Controller, into a Helper, or where it makes most sense for reuse. This way, your primary function can more easily be read as a list of calls to sub-functions vs having a waterfall of logic you cannot really wrap your head around. Rule of thumb - if a function is longer than your screen, refactor it into smaller parts. ;-)</p> </div> </div> <div class="page-bar-simple"> </div> <div class="footer"> <ul class="body"> <li>© <script> document.write(new Date().getFullYear()) </script> Githubissues.</li> <li>Githubissues is a development platform for aggregating issues.</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script> <script src="/githubissues/assets/js.js"></script> <script src="/githubissues/assets/markdown.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/languages/go.min.js"></script> <script> hljs.highlightAll(); </script> </body> </html>