laravelservice-layer

The effects of adding a Service Layer to a Laravel application


I have been developing with Zend Framework for a number of years and am now learning Laravel.

In my previous applications I usually have a Service Layer that is called by controllers. The Service Layer sits across the top of a Mapper and a Domain Model and is responsible for some application logic, raising events, some input filtering, etc.

Is there any reason why I should not implement a Service Layer in Laravel? In the examples that I have seen so far, controllers work directly with domain objects (or more accurately, active records).

If my Laravel controllers called my Service Layer, would I lose any of the advantages of Laravel? (As far as I can see I can still use Route/Model binding).

As a secondary question - what would be the best way to implement my Service Layer? As a collection of Service Providers, perhaps?


Solution

  • I also switched to Laravel coming from Zend and missed my Services. To sooth myself I have implemented a Service namespace which sits in namespace App\Services. In there I do all my Model or data handeling I don't want to see in my controller etc.

    An example of my controller layout:

    <?php
    namespace App\Http\Controllers;
    
    use App\Services\Contact as ContactService;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Lang;
    
    class IndexController extends Controller
    {
    
        /**
         * Create a new controller instance.
         *
         * @param Request $request
         * @return void
         */
        public function __construct(Request $request)
        {
            $this->_request  = $request;
        }
    
        /**
         * Standard contact page
         * 
         * @return contact page
         */
        public function contact(ContactService $contactService)
        {
            $errors  = null;
            $success = false;
            if ($this->_request->isMethod('post')) {
                $validator            = $contactService->validator($this->_request->all());
                if ($validator->fails()) {
                    $errors = $validator->errors();
                } else {
                    $contactService->create($validator->getData());
                    $success = true;
                }
            }
            return view('pages/contact', ['errors' => $errors, 'success' => $success]);
        }
    }
    

    The services return validators, handle cruds, basically do everything that I don't want to see in my Controller just like I had it in my Zend projects.

    Example of Service:

    <?php
    namespace App\Services;
    
    use Validator;
    use Mail;
    use App\Models\Contact as ContactModel;
    
    class Contact
    {
    
        /**
         * Get a validator for a contact.
         *
         * @param  array $data
         * @return \Illuminate\Contracts\Validation\Validator
         */
        public function validator(array $data)
        {
            return Validator::make($data, [
                    'email'     => 'required|email|max:255',
                    'phone'     => 'max:255',
                    'firstName' => 'required|max:255',
                    'lastName'  => 'required|max:255',
                    'message'   => 'required'
            ]);
        }
    
        /**
         * Create a new contact instance after a valid form.
         *
         * @param  array $data
         * @return ContactModel
         */
        public function create(array $data)
        {
            //Handle or map any data differently if needed, just for illustration
            $data = [
                'email'     => $data['email'],
                'firstName' => $data['firstName'],
                'lastName'  => $data['lastName'],
                'language'  => $data['language'],
                'phone'     => $data['phone'],
                'message'   => $data['message']
            ];
            
            // Send an email
            Mail::send('emails.contact', ['data' => $data], function ($m) use ($data) {
                $m->from(config('mail.from.address'), config('mail.from.name'));
                $m->to(env('MAIL_TO', 'hello@world.com'), env('MAIL_TO'))->subject('Contact form entry from: ' . $data['firstName']);
            });
            
            return ContactModel::create($data);
        }
    }