Jump to content
Sign in to follow this  
ICJ

Is this the correct implementation of a mvc service layer?

Recommended Posts

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.
 

Share this post


Link to post
Share on other sites

Some general comments.  M-V-C is ideal in team situations where you have multiple developers working on a similar problem.  Also, when building a framework or API where you plan to give other scripts or users access to certain features/functions.  In either instance, its very helpful when the code has strict adherence to the M-V-C paradigm.  If this is your own application and you don't expect to have other interact with your code, then having lax adherence (ie Service Layer) to M-V-C is personal preference.  

 

As for the script block above, it's hard (for me at least as an "outsider") to tell what's going on without see the implementation and/or application structure.  But if I were trying to explain M-V-C to someone, I would use the following scenario.  It helps to think about such abstractions in real world terms.  

 

SCENARIO:
Web user rents linens for her restaurant through a website.  She logs into the ordering system and is presented with a catalog of linens.  The system defines a default behavior which is to present a catalog of linens (including her previous order).  

 

The model first checked to determine if she was authorized to view the catalog.

Once satisfied, the model pulls all the relevant linen data; and her last purchase order.  

The model returns raw data objects to the controller.

 

The controller takes the raw data and processes the information in to formatted data objects. The controller applies filtering rules based on her ordering preferences and other routine functions. The data is stored in transport objects passed to the viewer for output.

 

The viewer creates an instance of the data objects through an interface method (typically defined in the controller) and outputs the result into document objects (DOM) ie product info with an images in some container element.

 

  ./catalog.php

           |_ catalog/src/cat_viewer.php

                          |_ catalog/src/cat_controller.php (includes catalog/lib/cat_model.php)

                                                                                                             |_ database

 

As both a PHP and Java developer, I find this structure to be the most common.  I doubt this answers your question, but hopefully it give some feedback.

Share this post


Link to post
Share on other sites

Your code looks good although I would move the redirect back to the controller and not handle it in your service.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.