What am i doing wrong? MVC structure

Ok so i have just started to learn MVC but am confused with how i get data from the controller to the view etc.

Her is my code for the view

<?php require APP_ROOT . '/views/inc/header.php' ?>
<?php flash('user_message'); ?>
    <div class="row mb-3">
        <div class="col-md-6">
            <h3>Users</h3>
        </div>
        <div class="col-md-6">
            <a href="<?php echo URL_ROOT; ?>/users/add" class="btn btn-primary pull-right">
                <i class="fa fa-pencil"></i> Add User
            </a>
        </div>
    </div>
	
	<?php 
	$data['role'] == $userDetails; 
	echo $userDetails->role;
	?>
	
	<?php echo $userDetails['role']; ?>
    <div class="card card-body mb-3">
        <div class="card-block">
            <div class="table">
                <table class="table table-sm table-condensed">
                    <thead>
                    <tr>
                        <th>Name</th>
                        <th>Email</th>
						<th>Role</th>
                        <th>Operation</th>
                    </tr>
                    </thead>
                    <tbody>
<?php foreach($data['users'] as $user) : ?>
                    <tr>
                        <td><?php echo $user->name; ?></td>
                        <td><?php echo $user->email; ?></td>
						<td><?php echo $user->role; ?></td>
                        <td><form action="<?php echo URL_ROOT; ?>/users/delete/<?php echo $user->id; ?>" method="post">
                            <input type="submit" value="Delete" class="btn btn-danger btn-sm">
                            </form>
                        </td>
                    </tr>
<?php endforeach; ?>
                    </tbody>
                </table>
            </div><!-- end div table-responsive -->
        </div>
    </div>
<?php require APP_ROOT . '/views/inc/footer.php' ?>

Here is my controller code

<?php


    class Users extends Controller
    {
        public function __construct()
        {
          $this->userModel = $this->model('User');
          $this->postModel = $this->model('Post');
        }
        
        public function index()
        {
            if(!isLoggedIn() ){
                redirect('users/login');
            }
			$userDetails = $this->userModel->getUserById($_SESSION['user_id']);
            $users = $this->userModel->getUsers();
            $data = [
                'users' => $users,
		'role' => $userDetails
            ];
            return $this->view('users/index', $data);
        }
}

Here is my model code

        public function getUserById($id)
        {
            $this->db->query('SELECT * FROM users WHERE id = :id');
            // Bind values
            $this->db->bind(':id', $id);
            return $this->db->single();
        }

I’m very confused with how i get the data across but individually and globally so please can someone tell me what i’m doing wrong as i started to learn MVC 3 days ago but i cant figure out how to get certain data suchg as the userDetails! So right now all im trying to show is the role id which is 2 but i just get errors! So can someone say what i’m doing wrong as why the MVC bit is not working.

Here is an image preview of the screen:

Thanks very much!
All help much appreciated!

Try omitting user-> and just use $name

Edit:
Also try this:

<?php foreach($users as $user) : 
echo '<pre>';
print_r($user);
echo '<\pre>';

// instead of this:

<?php foreach($data['users'] as $user) : ?>

Hi John, i was not clear enough!

This bit of code works fine: <?php foreach($data['users'] as $user) : ?> and loads in everything i need but this is for all users im trying to load individual users data in but i keep getting errors etc!

Here is the code im talking about:

(View page)

<?php

$data['snguser'] == $userDetails;

 echo $userDetails->role ;?>

(controller page)

        public function index()
        {
            if(!isLoggedIn() ){
                redirect('users/login');
            }
            $users = $this->userModel->getUsers();
			$userDetails = $this->userModel->getUserById($_SESSION['user_id']);
            $data = [
                'users' => $users,
				'snguser' => $userDetails,
            ];
            return $this->view('admin/index', $data);
        }

(model bit)

        public function getUserById($id)
        {
            $this->db->query('SELECT * FROM users WHERE id = :id');
            // Bind values
            $this->db->bind(':id', $id);
            return $this->db->single();
        }

Am i doing something wrong? Im trying to simply get the $data['snguser'] to equal a variable so i can do $userDetails->role; but its not working and for some reason its really confusing me! Am i doing arrays wrong or something?

Thanks very much

New error message:

Notice: Undefined variable: userDetails in C:\xampp\htdocs\framework\app\views\admin\index.php on line 33

Notice: Undefined variable: userDetails in C:\xampp\htdocs\framework\app\views\admin\index.php on line 35

Notice: Trying to get property 'role' of non-object in C:\xampp\htdocs\framework\app\views\admin\index.php on line 35

Did you intend this line to be 'userDetails' => $userDetails? It would explain why $userDetails is undefined in your view, because you’re actually assigning it to the name ā€œroleā€.

1 Like

Hi i changed it like so…

controller:

        public function index()
        {
            if(!isLoggedIn() ){
                redirect('users/login');
            }
            $users = $this->userModel->getUsers();
			$userDetails = $this->userModel->getUserById($_SESSION['user_id']);
            $data = [
                'users' => $users,
				'userDetails' => $userDetails,
            ];
            return $this->view('admin/index', $data);
        }

view:

<?php

$data['userDetails'] == $userDetails;

 echo $userDetails->role ;?>
<?php 

but unfortunately it still won’t work and i keep getting the same error!

Notice: Undefined variable: userDetails in C:\xampp\htdocs\framework\app\views\admin\index.php on line 33

Notice: Undefined variable: userDetails in C:\xampp\htdocs\framework\app\views\admin\index.php on line 35

Notice: Trying to get property 'role' of non-object in C:\xampp\htdocs\framework\app\views\admin\index.php on line 35

The thing that is so confusing tho is that this bit of code here works fine

foreach($data['users'] as $user) : ?>
                    <tr>
						<td><?php echo $user->username; ?></td>
                        <td><?php echo $user->name; ?></td>
                        <td><?php echo $user->email; ?></td>
						<td><?php echo $user->last_login; ?></td>
						<td><?php echo $user->role; ?></td>
						<td><?php echo $user->active; ?></td>
                        <td><form action="<?php echo URL_ROOT; ?>/admin/delete/<?php echo $user->id; ?>" method="post">
                            <input type="submit" value="Delete" class="btn btn-danger btn-sm">
                            </form>
							<?php if ($user->active == 1){ ?>
							<form action="<?php echo URL_ROOT; ?>/admin/ban/<?php echo $user->id; ?>" method="post">
                            <input type="submit" value="Ban" class="btn btn-danger btn-sm">
                            </form>
							<?php } elseif($user->active == 2){ ?>
							<form action="<?php echo URL_ROOT; ?>/admin/unban/<?php echo $user->id; ?>" method="post">
                            <input type="submit" value="Restore" class="btn btn-success btn-sm">
                            </form>
							<?php } else{ ?>
							<form action="<?php echo URL_ROOT; ?>/admin/activate/<?php echo $user->id; ?>" method="post">
                            <input type="submit" value="Manual Activate User" class="btn btn-success btn-sm">
                            </form>
							<?php } ?>
							<a href="<?php echo URL_ROOT; ?>/admin/edit/<?php echo $user->id; ?>">Edit User</a>
                        </td>
                    </tr>
<?php endforeach; ?>

so i just don’t understand why its not working! Its very odd!

Thanks again very much!

Try creating the following debug function because it is easy to type and shows all variables, arrays, etc:

//=====================
function fred( $val='Yes we have no $val', $title="No $title???")
{
  echo '<pre style="width:88%; margin:1em auto; background-color:snow; color:#000; padding:0.88em">';
echo $title .' ==> ';
  print_r( $val );  // maybe var_dump( $val );
  echo '</pre>';
 }

Usage:

  1. find the line error line and $variable
  2. at the line before type:
    fred( $variable, ā€˜Name of the $variable’);
    kill; // stop execution;

Are you using the CodeIgniter framework?

Hi, no this is a custom MVC framework! Here is a link to it on Github which i am building off of! The course is on Udemy!
https://github.com/almokhtarbr/speedy-Mvc

As i’m very new to MVC i thought id check out Udemy so i created a simple MVC from a tutorial but now im stuck with trying to do user roles because i could do sessions etc but peoples roles change so its more convenient just automatically changing rather than having to logout and then log back in again etc

Thanks

@jack55,
I had a quick look at the Udemey and could not find the particular tutorial. Is the tutorial free and what is the exact link?

Meanwhile I downloaded the GitHub MVC-Framework and managed to get most stuff working locally.

Unfortunately I am in the process of updating to the latest Linux 18.04 Bionic Beaver and have had a tremendous amount of problems. I have managed to use FileZilla to copy the files onto the remote server but unable to upload the database because I am locked out of PhpMyAdmin. I think the problem is the new SSH Keys. I will try reinstalling PhpMyAdmin tomorrow.

Meanwhile take a look at the Home and About pages because I have included an image which shows the MVC structure and how the Http requests are passed from the view to the controller, maybe cache, model etc It will be useful to trace back which files are causing your display errors.

https://www.johns-jokes.com/downloads/sp-i/jack55-mvc/

This part of your view looks wrong. It should be like the following:

$userDetails = $data['userDetails']; 
echo $userDetails->role;

This creates the $userDetails variable and assigns it the value from the userDetails in the $data variable.

Hi, i figured the reason out as to why it was not working!

The code needed was simply <?php echo $data['userDetails']->role; ?>


John here is a link to the MVC tutorial on Udemy – https://www.udemy.com/object-oriented-php-mvc/
I now have three remaining questions regarding this MVC!

  • Is it possible on an MVC to have a global bit of code that is always there for all controllers? otherwise it will have to be individual which is okay but im wondering whether there is a more sleek way!
        public function index()
        {
            if(!isLoggedIn() ){
                redirect('users/login');
            }
			$userDetails = $this->userModel->getUserById($_SESSION['user_id']);
            $users = $this->userModel->getUsers();
            $data = [
				'userDetails' => $userDetails,
                'users' => $users
            ];
            return $this->view('users/index', $data);
        }

And the second one is how would you recommend i integrate a roles and permissions into this MVC sleekly and easily!
I’m looking at these tutorials and basically i just want to know the best way to implement them so it sort of goes with the above question regarding a global controller etc?

Finally with the view controller it looks like this so right now it loads in pages/index.php straight away as the index.php but then when you go to other pages such as about it goes pages/about.php so is there a way to say if its pages then allow the link to work without pages so it just goes name.com/about like it does with just name.com which loads the pages/index?

<?php
    /*
    * App Core Class
    * Create URL & load core controller
    * URL FORMAT - /controller/method/params
    */

    class Core
    {
        protected $currentController = 'Pages';
        protected $currentMethod = 'index';
        protected $params = [];

        public function __construct()
        {
            $url = $this->getUrl();

            // Look in controllers for first value
            if (file_exists('../app/controllers/' . ucwords($url[0]) . '.php')) {
                // If exists, set as controller
                $this->currentController = ucwords( $url[0] );
                // Unset 0 url
                unset($url[0]);
            }

            // Require the controller
            require_once '../app/controllers/' . $this->currentController . '.php';

            // Instantiate controller class
            $this->currentController =  new $this->currentController;

            // Check for second part of url
            if (isset($url[1])) {
                if(method_exists($this->currentController, $url[1]))
                {
                    $this->currentMethod = $url[1];
                    unset($url[1]);
                }
            }

            // Get params
            $this->params = $url ? array_values($url) : [];

            //Call a callback with array of params
            call_user_func_array([$this->currentController, $this->currentMethod], $this->params);
        }

        public function  getUrl()
        {
            if (isset($_GET['url'])) {
                $url = rtrim($_GET['url'],'/');
                $url = filter_var($url,FILTER_SANITIZE_URL);
                $url = explode('/',$url);
                return $url;
            }

        }

    }

Thanks ever so much and i know this is alot of questions but i’m just trying to learn a better and more secure way to code and i below MVC is the first step! I now understand around 80% of MVC just the few bits i dont understand are the bits limiting me as all mentioned above! Thanks again

Your three questions:

Q1. Is it possible on an MVC to have a global bit of code that is always there for all controllers?

Yes check out the videos about class Pages extends Controller

Q2. And the second one is how would you recommend i integrate a roles and permissions into this MVC sleekly and easily!

How far have you progressed with the tutorials? Have you created the database tables and classes?

Q3. allow the link to work without pages so it just goes name.com/about like it does with just name.com which loads the pages/index?

Yes check out .htaccess file and also the way the controllers handle the pages URI.

I managed to get the demo site running and it has the Flowchart image which makes it easier to visualise what happens with a page link request. Link in Post #8

1 Like

Hi John,

Are you talking about the course? Did you buy it also?

I have reached the end and am now doing custom work on the MVC so i have created a profile page, change password and show all users. Now i don’t know where to start regarding implementing roles efficiently and seemlessly.

I have John and it goes like this call_user_func_array([$this->currentController, $this->currentMethod], $this->params); but i’m trying to find the best way to make it like an array so that its almost like this (a quick mockup)

	'account/login' => [
		'controller' => 'account',
		'action' => 'login',
	],
	'account/register' => [
		'controller' => 'account',
		'action' => 'register',
	],
	'account/register/{ref:\w+}' => [
		'controller' => 'account',
		'action' => 'register',
	],
	'account/recovery' => [
		'controller' => 'account',
		'action' => 'recovery',
	],
	'account/confirm/{token:\w+}' => [
		'controller' => 'account',
		'action' => 'confirm',
	],
	'account/reset/{token:\w+}' => [
		'controller' => 'account',
		'action' => 'reset',
	],
	'account/profile' => [
		'controller' => 'account',
		'action' => 'profile',
	],

Thanks again also would you ever be able to do Discord or something?

1 Like

No I did not buy the course, I just looked at the sample videos. I did download the provided link from GitHub and updated my web-page. It was hard going trying to eliminate /pages/ from the URI but eventually I managed. The script is OK to learn MVC basics but very limited compared with the features available with CodeIgniter. I cannot expect many updates and would recommend using the skills gained to test other PHP Frameworks which are regularly maintained with security updates.

I have no knowledge of using roles. The tutorial links seem comprehensive and I think it is only a matter of spending some time trying to integrate the scripts into your current project.

What is Discord? I tried searching and could not find a satisfactory solution.

[off-topic]
Discord is an application used by groups for messaging, sharing information and resources. I belong to a web development meet-up that makes use of a Discord account. It’s at discordapp.com
[/off-topic]

You said you managed to remove ā€˜pages’ please can you explain how you did it?

Discord is chat application but don’t worry

Thanks

It was quite involved and a lot of the script had to be rewritten, navbar.php had to have the pages/ removed and also Core.php required this additional script:

  # Check if second part of url is set (method)
    if(isset($url[1]))
    {
      // Check if method/function exists in current controller class
      if(method_exists($this->currentController, $url[1]))
      {
        # fred($url, __line__);
        # Set current method if it exsists
        $this->currentMethod = $url[1];
        // Unset 1 index
        unset($url[1]);
      }
    }
    if(isset($url[0]))
    {
      # fred($url, __line__);
      // Check if method/function exists in current controller class
      if(method_exists($this->currentController, $url[0]))
      {
        // Set current method if it exsists
        $this->currentMethod = $url[0];
        // Unset 1 index
        unset($url[0]);
      }
    }

  # Get params - Any values left over in url are params
  # DEFAULT 
  # controller -> Pages.php
  # params     -> about
  $this->params = $url ? array_values($url) : [];
]// end public function __construct()


I introduced this script into index.php which helped tremendously:


<?php
	declare(strict_types=1);

# ERROR STUFF
	define('LOCALHOST', 'localhost'===$_SERVER['SERVER_NAME']);
	if(LOCALHOST):
		ini_set('display_errors', 'true');
		ini_set('display_html_errors', 'true');
		ini_set('display_startup_errors', 'true');
	endif;	
	error_reporting(-1);

# require_once('../app/bootstrap.php');
  require_once('../app/bootstrap.php');

# Init Core Library
 	$init = new Core;

and also added this at the end of navbar:

  <?php 
    if(LOCALHOST):
      require '../app/views/inc/debug.php'; 
    endif;  
  ?>
  <!-- collapse navbar-collapse </div> -->
<!-- container </div> -->

file: app//views/inc/debug.php

<?php 
	# debug.php

	$fff 		= __file__;
	$APPROOT 	= APPROOT;
	$URLROOT 	= URLROOT;
	$session    = $_SESSION['user_id'] ?? 'NOT LOGGED IN';
	$online     = '//www.johns-jokes.com/downloads/sp-i/jack55-mvc/';

	$tmp = <<< ____TMP
	<div class="dib w88 mga tac">
		<h5 class="tal"> app/view/inc/debug.php </h5>
		<h6 class="tal"> <a href="$online"> ONLINE </a> </h6>
		<table class="dib tal bgy">
			<tr><td> File     </td><td> $fff 	  </td></tr>
			<tr><td> URLROOT  </td><td> $URLROOT  </td></tr>
			<tr><td> APPROOT  </td><td> $APPROOT  </td></tr>
			<tr><td> \$url    </td><td> $url 	  </td></tr>
			<tr><td>\$session </td><td> $session  </td></tr>
			<tr><td> spare    </td><td> spare     </td></tr>
		</table>
		<p> &nbsp; </p>
	</div>	
____TMP;
echo $tmp;

It took quite some time but I was determined to finish. Now looking to return t CodeIgniter because it is so much easier. Core models do not require modifications.

Have you an online version or just working locally?

i’m working locally atm but im planning on moving to Codeigniter once version 4 is out! I dislike Laravel due to the composer requirement and i just prefer the simplicity of Codeigniter 10x but what im building right now is a simple blog system that does not need the complexities of a proper framework which is why i’m doing my own!

Also after all the hard work you did getting the pages to work, do you think it would of been easier for you to do an array class that does custom routing?

But thanks again!

1 Like

I have had CI4 running successfully using an online server with a free domain for quite a few months. MySql and Ajax works without any problems and I believe the delay in releasing the beta version is having to implement other database systems. As far as working locally is concerned I usually find that uploading to a remote server introduces inconsistencies. These are far better to be resolved asap before they escalate requiring drastic changes to ensure the the script works remotely.

Did you have any success with implementing your custom array class solution?

Regarding the custom routes option i unfortunately did not manage to get it working but i believe im kind of close in the sense that im like 15% getting there! Right now I had to change quite a lot which i think is a bad thing and im sure you probs would do it in like two lines of code seeing as you seem much more experienced than me but basically at the end of the day i want it to work the way it is right now and also a custom route as well if i want to shorten it etc.

Here is the routes.php file:

$route['default'] = 'home';

$route['user/([a-z]+)/([a-z]+)'] = 'home/user/$1/$2';
$route['page/([a-z]+)/([a-z]+)'] = 'admin/my-admin/page/$1/$2';
$route['anything/:any/:any'] = 'home/index/$1/$2';
$route['number/:num/:num'] = 'home/index/$1/$2';
$route['product/(:num)'] = 'catalog/product_lookup_by_id/$1';
//A URL with ā€œproductā€ as the first segment, and a number in the second will be remapped to the ā€œcatalogā€ controller class and the ā€œproduct_lookup_by_idā€ method passing in the match as a variable to the method.

changed the core.php


        // Rebase Parameters or set empty array.
        $this->params = $this->url ? array_values($this->url) : [];

        // Send Parameters to the Method of the Controller.
        call_user_func_array([$this->controller, $this->method], $this->params);
    }


    protected function dashesToCamelCase($string, $capitalizeFirstCharacter = true)
    {
        $str = str_replace(' ', '', ucwords(str_replace('-', ' ', $string))); // < php5.5.9
        //$str = str_replace('-', '', ucwords($string, '-')); // > php5.5.9

        if (!$capitalizeFirstCharacter) {
            $str = lcfirst($str);
        }

        return $str;
    }

    protected function remapUrl($url, $route)
    {
        foreach($route as $pattern => $replacement)
        {
            $pattern = str_replace(":any", "(.+)", $pattern);
            $pattern = str_replace(":num", "(\d+)", $pattern);
            $pattern = '/' . str_replace("/", "\/", $pattern) . '/i';
            $replacement = '/'.$replacement.'/';
            $route_url = preg_replace($pattern, $replacement, $url);
            if($route_url !== $url && $route_url !== null) {
                return $route_url;
            }
        }
        return $url;
    }

but personally i believe im over complicating it but Thanks again John!

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.