5
\$\begingroup\$

I am building my own MVC structured project to learn the concepts of MVC and I need to pass $_POST variables from my view (which is a login form) to the model. Down below is my approach but I wonder if it is the correct way to do it or there is a better one for example passing it into the construct or maybe putting $_POST inside an array.. What do you think?

The login method is to generate a token for my login form in the view which is for the cookie

class Users
{    

    protected $db;

    public function __construct() 
    {
        $this->db = Database::instance();
    }

    public function login()
    {
        return self::genToken();        
    }

    public function loginAction()
    {
        $username   = $_POST['username'];
        $password   = $_POST['password'];
        $rememberMe = $_POST['remember_me'];
        $token      = $_POST['token'];
        $redirect   = null;
        ...

    }
    public function genToken()
    {
       return md5(uniqid());
    } 

Here is my view. (http://localhost/MVC/public/user/login)

<form id="login" method="post" action="http://localhost/MVC/public/user/loginAction" class="well">
    <div class="form-group">
        <label>Username</label>
        <input type="text" name="username" class="form-control" placeholder="">
    </div>
    <div class="form-group">
        <label>Password</label>
        <input type="password" name="password" class="form-control" placeholder="Password">
        <input type="hidden" name="token" value="<?= $data['token'] ?>" />
        <div class="help-block text-right"><a href="#">Forget the password?</a></div>
    </div>
    <div class="checkbox">
        <label><input type="checkbox" name="remember_me" value="1" checked /> Remember Me</label>
    </div>
    <button type="submit" class="btn btn-default btn-block" name="signin">Login</button>
</form>
\$\endgroup\$
  • \$\begingroup\$ Please try to avoid using superglobals in your methods. Better to pass them in if possible. \$\endgroup\$ – I wrestled a bear once. Aug 2 '17 at 22:03
  • \$\begingroup\$ Could you give me an idea on how would I do this? \$\endgroup\$ – user3628807 Aug 2 '17 at 22:08
  • \$\begingroup\$ I would but I don't know how your users class is being called so I can't. Besides it's just a general rule of thumb. The general exception is if it's a controller class sometimes you can get away with it. You should consider doimg some input sanitation though. \$\endgroup\$ – I wrestled a bear once. Aug 2 '17 at 22:10
  • \$\begingroup\$ And it's really hard for me to explain what is happening because I saw it in a video tutorial and I don't fully understand it, but here is my controller user.php. My Controller class and my App.php. I think that is enough to get the gist of what is going on. \$\endgroup\$ – user3628807 Aug 2 '17 at 22:20
  • \$\begingroup\$ Is it really going to benefit you if I review and make suggestions on code you don't fully understand? You should seek to understand the code first. \$\endgroup\$ – I wrestled a bear once. Aug 2 '17 at 22:45
4
\$\begingroup\$

Superglobals

I cloned your repo and it looks like you've already taken my advice about passing in the superglobals instead of using them directly from your methods.

Formatting

I would have also commented on the formatting but you've also significantly improved that since posting the question. Cheers!

Commenting

The next good habit to get into is commenting, which s just as important as formatting and entirely lacking in this project. You should always put a short comment above every method explaining what it does, what parameters it takes and what it returns so you (and people maintaining the code in the future) don't have to read the code to figure out what it does.

I see you're using NetBeans. I Like NB too. NB makes documenting your PHP and JS classes and functions very easy. I looked and I could not find any good documentation because I cannot remember what it's called, so I'll just explain the features as best I can.

Auto-commenting Pro Tip

Netbeans will actually do most of the work for you - place the cursor on the empty line just above the start of the function and type /** and hit the enter key. Netbeans will create the rest of the comment including the parameters and the return type (if there is one).

This is what Netbeans generated for your loginAction method.

/**
 * 
 * @param type $username
 * @param type $password
 * @param type $rememberMe
 * @param type $token
 * @param type $redirect
 */
public function loginAction($username, $password, $rememberMe, $token, $redirect = null)

Now you just need to write a short description on the first blankk line and fill in the types (if NB wasn't able to guess them already). eg..

/**
 * Authenticate a user and start a session
 * @param String $username - The username of the user
 * @param String $password - The user's password
 * @param Boolean $rememberMe - Set a cookie?
 * @param String $token - Login token
 * @param String|Null $redirect - URL to redirect to
 */
public function loginAction($username, $password, $rememberMe, $token, $redirect = null)

Now, not only will this be easy to read and maintain, but NetBeans will actually parse these comments and show you what parameters are required when you start to type the method name:

enter image description here

\$\endgroup\$
2
\$\begingroup\$

NOTE! The text below is how i understand MVC it doesn't have to be completely right!

From what i have seen in your project you are missing the following things:

  • Namespaces
  • Bad project Organization
  • Mixing logic
  • You are abstracting PDO
  • And a bunch of small thing...

MVC stands for Model View Controller, and we will pass each of thise and explain what which holds.

The main reason behind it is that we Soc the project, and that we have an easier project organizaion.Tho it is not good for smaller projects since it can be an overkill.


We could separate the MVC architecture into two parts:

  • Presentation logic (Router,Dispatcher,Bootstrap,Controllers,Views)
  • Business logic/Domain logic (Services,Mapper,Entities/Domain Objects...)

In this graph below it's explained how a well structured MVC project should look like:

enter image description here

Basically the router gets your input, the dispatcher gets the data thats being sent and sends it to the controller. The controller calls the service, the service puts the data in an entity/domain object, constructs a mapper and sends that entity to that mapper. The mapper communicates to the database.


Here are some references what to use:

  • Slim 3 for routing + dispatcher
  • Slim 3 for DiC(will be used to create the controllers and services in the bootstrap.It uses pimple)
  • Yaml for some configuration stuff
  • Composer for autoloading and much more

Here is a small kick start that will pass each of thise steps:

Step 1:

Create a project via composer and setup the following project structure

|-public
|      \index.php
|-src
|   \ProjectName
|              \Core
|              \Models
|              \Presentation
|              \Router

Step 2:

Setup Slim 3 DiC for easier controller and service loading.When it's up create the router it self.

Should look similar to this:

    //Setup Apps Controller
    $this->container['appsController'] = function () {
        $appController = new Controllers\AppsController(
            $this->container->get('appsService'),
            $this->container->get('logger'),
            $this->container->get('view')
        );
        return $appController;
    };
    //Setups Apps Service
    $this->container['appsService'] = function ($mapperFactory) {
        return new Service\Apps($this->container->get('mapperFactory'));
    };

Than in the router:

    //App Routes    
        $this->app->group(self::URL.'apps', function () {

            //Get App Packages
            $this->get('/{app_id}/packages', function ($request, $response, $args) {
                //Use Apps Controller Dependency
                $appsDependency = $this->get('appsController');
                //Call the Controllers Function
                $appsDependency->getAppPackages($request,$response, $args);
            });
...

Step 3:

Here is an example of how should the controller look like:

<?php

namespace Discus\Presentation\Controllers;

use Discus\Models\Service\Apps as Apps;

class AppsController
{

    protected $logger;
    protected $view;
    protected $apps;

    public function __construct(Apps $apps,$logger,$view)
    {
        $this->apps = $apps;
        $this->logger = $logger;
        $this->view = $view;
    }

    public function getApps($request,$response, $args)
    {
        $getApps = $this->apps->getApps();
        //OutPut Json
        $this->view->renderJsonView(['data'=>$getApps],$response);
    }

From the controller you call the service:

Step 4:

Service example:

<?php

namespace Discus\Models\Service;

use Discus\Models\Entities\App as App;
use Discus\Core\Mapper\CanCreateMapper; 
use Discus\Models\Mapper as Mapper;

class Apps
{

    private $factory;

    public function __construct(CanCreateMapper $factory)
    {
        $this->factory = $factory;
    }

    /**
    * Get All Apps 
    */
    public function getApps()
    {
        $apps = new App\App;

        $appsMapper = $this->factory->create(Mapper\Apps::class);
        $appsMapper->fetch($apps);

        return $apps->getApps();
    }

You build the mapper here that you will be using and you populate the entity data(if any), and you send it to the mapper.

Step 5:

The mapper:

class Apps extends DataMapper
{

    /**
    * Fetch All Apps
    */
    public function fetch(App\App $app)
    {   
        $sql = "SELECT 
                 id_app as id,
                 name,
                 package_name
                 FROM app ORDER BY id_app DESC";

        $statement = $this->connection->prepare($sql);
        $statement->execute();

        $data = $statement->fetchAll(PDO::FETCH_ASSOC);

        foreach($data as $data){
            $app->setApps($data);
        }

    }

See in this part that there is no abstraction to the PDO since there is no sense for abstracting it.

And her is how the CanCreateMapper looks like:

It's just an interface:

<?php

namespace Discus\Core\Mapper;

interface CanCreateMapper
{

    public function create(string $className);

}

Here is the DataMapper:

use PDO;

abstract class DataMapper
{

    protected $connection;
    protected $configuration;

    public function __construct(PDO $connection, array $configuration)
    {
        $this->connection = $connection;
        $this->configuration = $configuration;
    }

}

Here is the MapperFactory:

use PDO;

class MapperFactory implements CanCreateMapper
{
    private $connection;
    private $cache = [];
    private $configuration;

    public function __construct(PDO $connection, array $configuration)
    {
        $this->connection = $connection;
        $this->configuration = $configuration;
    }

    public function create(string $className): DataMapper
    {

        if (array_key_exists($className, $this->cache)) {
            return $this->cache[$className];
        }

        if (!class_exists($className)) {
            die("Mapper not found. Attempted to load '{$className}'.");
        }

        $instance = new $className($this->connection, $this->configuration);
        $this->cache[$className] = $instance;

        return $instance;
    }

}

It's a solid kick start for you and you will be able to learn a lot from this.

Also some you should check out PSR and Monolog.

Cheers :D

\$\endgroup\$
  • \$\begingroup\$ Wow, thank you for your detailed response! I will definitely take a better look at your examples but I think it's a little bit too overwhelming for me. Using Slim and whatnot. :D I am just trying to learn the basics of MVC and what handles what. \$\endgroup\$ – user3628807 Aug 3 '17 at 19:09
  • \$\begingroup\$ @user3628807 if you spend more time reading though the thing i wrote within 7 days you will understand all of it.I recommend that you start of with composer and slim. \$\endgroup\$ – DaAmidza Aug 3 '17 at 19:14
  • \$\begingroup\$ Also if it was helpful accept the answer :D \$\endgroup\$ – DaAmidza Aug 3 '17 at 19:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.