3
\$\begingroup\$

I wrote a series of questions in a previous post, and a user recommended that I separate the questions.

I have made the switch from procedural programming to object-oriented programming. I have grasped/utilized the benefits of OOP, but I am still struggling with the placement of the business logic for the application.

I will provide an example of my user registration process excluding routing/dependency injection/autoloader, etc.

Controller

<?php

/**
 *-----------------------------------------------------------------
 * 
 *  USER REGISTRATION CONTROLLER
 * 
 */
namespace Controller\www;
use \Helper\Controller;

class Register extends Controller {
    public $dependencies = ['arena', 'vars'];

    /**
     *  Registration Form
     * 
     *  @param mixed $referrer      Userid || Username of Referrer Pulled From Uri
     */
    public function index($referrer = '') {    

        // User Already Logged In Redirect
        $this->user->id ? $this->redirect->home() : '';


        // Define Default
        $username   = '';        
        $email      = '';
        $timezone   = '';
        $dob        = [
            'month' => '',
            'day'   => '',
            'year'  => ''
        ];
        $referrer   = $referrer;


        /**
         *  Registration Form Posted
         */
        if ($this->form->post('register')) {

            // Define and Sanitize Post Data
            $username   = $this->input->get('username');           
            $email      = $this->input->get('email', 'post', 'email');           
            $password   = [
                'password'  => $this->input->get('password'),
                'confirm'   => $this->input->get('confirmPassword')
            ];

            $timezone   = $this->input->get('timezone');
            $dob        = [
                'month'     => $this->input->get('month'),
                'day'       => $this->input->get('day'),
                'year'      => $this->input->get('year')
            ];

            $referrer   = $this->input->get('referrer');
            $botcheck   = $this->input->get('vu');


            /** 
             *  If      Successful,Register User, Login User, Redirect Home
             *  Else    Set Error Alerts 
             */
            $this->factory->make('register/user')->post($username, $email, $password, $timezone, $dob, $referrer, $botcheck);
        }


        /**
         *  Define Site Title & Display Page
         */
        $this->view->sitetitle('register');
        $this->view->display('www/register', [
            'video'     => $this->arena->video(),
            'username'  => $this->input->set($username),
            'email'     => $this->input->set($email),
            'timezone'  => $this->input->set($timezone),
            'month'     => $this->input->set($dob['month']),
            'day'       => $this->input->set($dob['day']),
            'year'      => $this->input->set($dob['year']),
            'referrer'  => $this->input->set($referrer),

            'timezones' => $this->vars->timezones,
            'months'    => $this->vars->months,
            'days'      => $this->vars->days,
            'years'     => $this->vars->years
        ]);
    }
}

Service layer

<?php

/**
 *-----------------------------------------------------------------
 * 
 *  USER REGISTRATION SERVICE
 *  
 *  Handles Business Logic For Registration Process
 * 
 */
namespace Model\Services\Register;
use \Helper\Service;

class User extends Service {
    public $dependencies = ['login', 'restricted', 'session', 'time', 'user', 'vars'];

    /**
     *  Handles Entire Registration Process For Site Users
     *  
     *  @params all         User Submitted Form Data
     */
    public function post($username = '', $email = '', $password = '', $timezone = '', $dob = '', $referrer = '', $botcheck = '') {

        // Validate $_POST Form Data
        $this->validateInput($username, $email, $password, $timezone, $dob, $botcheck);


        /** 
         *  No Errors Produced - Complete Form Submission
         */
        if (!$this->alert->errors()) {

            // Create New User
            $this->user->create($username, $email, $password['password'], $dob, $timezone);

            // Login User                                                                                                                         
            $this->login->user();                                                                                                                            

            // Set Alert & Redirect
            $this->alert->info('Account created successfully');                                             
            $this->redirect->home();                                                                              
        }  
    }


    /**
     *  Validate Registration $_POST Data
     * 
     *  @params all         User Submitted Form Data
     */
    private function validateInput($username = '', $email = '', $password = '', $timezone = '', $dob = '', $botcheck = '') {

        // Validate Username
        if (!$username) {                       
            $this->alert->error('Please enter a username');                                    
        } 
        elseif ($this->restricted->check($username)) {                    
            $this->alert->error('The username ' . $this->input->set($username) . ' is unavailable');       
        } 
        elseif (strlen($username) > 20) {        
            $this->alert->error('Usernames cannot exceed 20 characters');                      
        }


        // Validate Email
        if (!$email) {                                                                    
            $this->alert->error('Please enter an email address');                                                
        } 
        elseif(!$this->input->validate($email)){ 
            $this->alert->error('The email you provided is invalid'); 
        } 
        elseif ($this->user->exists(['email' => $email])) {                                                           
            $this->alert->error('The email you provided is already in use');                                     
        }


        // Validate Password
        if (!$password['password'] || !$password['confirm']) {                                      
            $this->alert->error('Please enter a password');                                                      
        } 
        elseif ($password['password'] !== $password['confirm']) {                                               
            $this->alert->error('Passwords do not match');                                                       
        } 


        // Validate Timezone
        if (!$timezone AND $timezone != 0) {                                              
            $this->alert->error('Please select a timezone');                                                     
        } 
        elseif (!array_key_exists($timezone, $this->vars->timezones)) {                                                
            $this->alert->error('The selected timezone is invalid.');                                          
        }


        // Validate Date of Birth
        if (!$dob['month'] || !$dob['day'] || !$dob['year']) {                                     
            $this->alert->error('Date of Birth cannot be left empty');                                           
        } 
        elseif (
            !in_array($dob['month'],    $this->vars->months)  || 
            !in_array($dob['day'],      $this->vars->days)    || 
            !in_array($dob['year'],     $this->vars->years)
        ) {      
            $this->alert->error('The selected date of birth is invalid.');                                     
        }


        // If Not Empty Bot Auto-Filled Form
        if ($botcheck) {                                                               
            $this->alert->error('Our system flagged you as a bot when submitting the form. Please try again.');  
        }
    }
}

Previously (procedural days), my .htaccess would route to a file (which is what I would consider the controller) and that file would instantiate classes such as db, or utilities, and it contained both the controller and service layer. I was not using models and I just placed everything in that specific file so I was used to duplicating code all over the place.

My current models (see file directory below) handle all of the db interaction and chained db transactions. For example, $this->login->user() sets the session data containing the user ID, user key, and token (created by non-sensitive data). This will also store the 'pieces' that make up the key and token into the db, upon login a few expiration/user updates will take place, etc.

So essentially my service layers are responsible for processing user input (sometimes processing output for view as well) and it then either hands the data back to the controller to output to view or it will send to model to update/make changes in the db.

Is there a fundamental flaw or issue that i am not seeing? Is my understanding of mvc and the separation appropriate/accurate?

I am not having any problems with this approach but I do not want to learn an anti-pattern, etc. get used to it and adapt my learning/experience around it. One of the reasons why I made the models a bit general is to allow for use in other places. For example once a user registers the appropriate data is passed to the login model and then $this->login->user() is called to set all login creds, etc.

Should my model contain all of the service layer code?

I have read on other sites that the model should contain the logic similar to my service layer. The site is HEAVILY db/user driven so the models are pretty loaded as it is with all the db interactions. So I just don't see this being the appropriate approach to take.

For those of you who will tell me not to reinvent the wheel, etc. This entire project has been used as a learning experience to transition from procedural -> OOP once I have a firm grasp on everything I will explore frameworks, etc.

I also wanted to mention that I fully understand that everyone gets used to/develops an MVC application a bit different BUT that does not take away from the core principles of appropriate separation of concerns, and the way things should generally be handled. I am asking for feedback to re-affirm/define whether or not I have an appropriate understanding of everything, my style will obviously change down the line once/if decide to adopt a framework.

\$\endgroup\$
  • \$\begingroup\$ I really really think you should take a look at a popular framework, and see how they have solved these issues, because you are in danger of getting quite lost. Trying to learn every modern OOP concept you have heard of in one go it destined to fail. And as nobody but you has any concept how your program is supposed to work, you will find it very hard to get help from places like here. \$\endgroup\$ – Steve Jun 23 '15 at 15:23
  • \$\begingroup\$ Whereas if your question revolved around a popular framework, many people would at least recognize the components so be able to better understand what you want to achieve. \$\endgroup\$ – Steve Jun 23 '15 at 15:25
  • 2
    \$\begingroup\$ Lastly, when googling for architectural questions and theories, i personally find including php in the search is a great way to get poor or misleading results. I find i get much better results either leaving out the specific language, or deliberately searching for c#/java examples \$\endgroup\$ – Steve Jun 23 '15 at 15:32
  • \$\begingroup\$ With all of my learning i watch/follow tutorials for popular frameworks. I watched a laracast video "Controller Cleanup" and they throw the logic into a service layer. Most of my learning has come from following laravel videos so i am going to make the switch soon. I will try searching without the php keyword thank you. \$\endgroup\$ – ICJ Jun 23 '15 at 21:30

Your Answer

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

Browse other questions tagged or ask your own question.