• Stars
    star
    246
  • Rank 164,726 (Top 4 %)
  • Language
    PHP
  • License
    MIT License
  • Created over 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A package to help you clean up your controllers in laravel

πŸ”₯Laravel Terminator πŸ”₯

πŸ’Ž "Tell, don't ask principle" for your laravel controllers

What this package is good for?

Short answer : This package helps you clean up your controller code in a way that you have never seen before

Latest Stable Version Build Status Quality Score License Total Downloads

**Made with ❀️ for every laravel "Clean Coder"**

Installation:

composer require imanghafoori/laravel-terminator

No need to add any service providers.

Compatibility:

  • Laravel +5.1 and above
  • Php 7.0 and above

When to use it?

Code smell: πŸ‘ƒ

  • When you see that you have an endpoint from which you have to send back more than one type of response... Then this package is going to help you a lot.

Example:

Consider a typical login endpoint, It may return 5 type of responses in different cases:

  • 1- User is already logged in, so redirect.
  • 2- Successfull login
  • 3- Invalid credentials error
  • 4- Incorrect credentials error
  • 5- Too many login attempts error

The fact that MVC frameworks force us to "return a response" from controllers prevents us from simplify controllers beyond a certain point. So we decide to break that jail and bring ourselves freedom.

The idea is : Any class in the application should be able to send back a response.

Remember:

Controllers Are Controllers, They Are Not Responders!!!

Controllers, "control" the execution flow of your code, and send commands to other objects, telling them what to do. Their responsibility is not returning a "response" back to the client and this is the philosophy of terminator package.

Consider the code below:

// BAD code : Too many conditions
// BAD code : In a single method
// BAD code : (@_@)   (?_?)
// (It is not that bad, since it is a simplified example)
class AuthController {
  public function login(Request $request)
  {
           
           $validator = Validator::make($request->all(), [
              'email' => 'required|max:255||string',
              'password' => 'required|confirmed||string',
          ]);
          
          if ($validator->fails()) {
              return redirect('/some-where')->withErrors($validator)->withInput(); // return response 1
          }
          
         
          // 2 - throttle Attempts
          if ($this->hasTooManyLoginAttempts($request)) {
              $this->fireLockoutEvent($request);
              return $this->sendLockoutResponse($request);   // return response 2
          }
        
         
          // 3 - handle valid Credentials
          if ($this->attemptLogin($request)) {
              return $this->sendLoginResponse($request);   // return response 3
          }
        

          // 4 - handle invalid Credentials
          $this->incrementLoginAttempts($request);
          return $this->sendFailedLoginResponse($request); // return response 4
          
          
          //These if blocks can not be extracted out. Can they ?
  }
}

Problem:

With the current approach, this is as much as we can refactor at best. Why? because the controllers are asking for response, they are not telling what to do.

We do not want many if conditions all within a single method, it makes the method hard to understand and reason about.

// Good code
// Good code
// Good code

class LoginController
{
    public function Login(Request $request)
    {
        // Here we are telling what to do (not asking them)
        // No response, just commands, Nice ???
        
        $this->validateRequest();          // 1
        $this->throttleAttempts();         // 2
        $this->handleValidCredentials();   // 3 
        $this->handleInvalidCredentials(); // 4
        
    }
    
    // private functions may sit here
    
    ...
    
}

Note:

Using "respondWith()" does not prevent the normal execution flow of the framework to be interrupted. All the middlewares and other normal termination process of the laravel will happen as normal. So it is production ready! 🐬

Refactoring Steps: πŸ”¨

1 - First, you should eliminate "return" statements in your controllers like this:

use \ImanGhafoori\Terminator\Facades\Responder;

class AuthController {
    public function login(Request $request)
    {
           // 1 - Validate Request
           $validator = Validator::make($request->all(), [
              'email' => 'required|max:255||string',
              'password' => 'required|confirmed||string',
          ]);
          
          if ($validator->fails()) {
               $response = redirect('/some-where')->withErrors($validator)->withInput();
               respondWith($response);  // <-- look here
          }
          
         
          // 2 - throttle Attempts
          if ($this->hasTooManyLoginAttempts($request)) {
              $this->fireLockoutEvent($request);
              $response = $this->sendLockoutResponse($request);
              respondWith($response); // <-- look here "no return!"
          }
          
         
          // 3 - handle valid Credentials
          if ($this->attemptLogin($request)) {
               $response = $this->sendLoginResponse($request);
               respondWith($response);  // <-- look here  "no return!"
          }
          

          // 4 - handle invalid Credentials
          $this->incrementLoginAttempts($request);
          $response = $this->sendFailedLoginResponse($request) 
         
          respondWith($response);  // <-- look here "no return!"
    }
}

Do you see how "return" keyword is now turned into regular function calls?!

2 - Now that we have got rid of return statements,then the rest is easy, It is now possible to extract each if block into a method like below:

class LoginController
{
    public function Login(Request $request)
    {
        $this->validateRequest();         
        $this->throttleAttempts();       
        $this->handleValidCredentials();  
        $this->handleInvalidCredentials(); 
        
    }
    ...
}

Terminator API

All this package exposes for you is 2 global helper functions and 1 Facade:

  • respondWith()
  • sendAndTerminate()
  • \ImanGhafoori\Terminator\TerminatorFacade::sendAndTerminate()
$response = response()->json($someData);

respondWith($response);

// or 
respondWith()->json($someData);


// or an alias function for 'respondWith()' is 'sendAndTerminate':

sendAndTerminate($response);


// or use facade:
\ImanGhafoori\Terminator\TerminatorFacade::sendAndTerminate($response);

In fact sendAndTerminate() ( or it's alias "respondWith" ) function can accept anything you normally return from a typical controller.

About Testibility:

Let me mention that the "sendAndTerminate or respondWith" helper functions (like other laravel helper functions) can be easily mocked out and does not affect the testibility at all.

// Sample Mock
TerminatorFacade::shouldRecieve('sendAndTerminate')->once()->with($someResponse)->andReturn(true);

In fact they make your application for testable, because your tests do not fail if you change the shape of your response.

How The Magic Is Even Possible, Dude?!

You may wonder how this magic is working behind the scenes. In short it uses nothing more than a standard laravel "renderable exception".

We highly encourage you to take a look at the simple source code of the package to find out what's going on there. It is only a few lines of code.

More from the author:

Laravel HeyMan

πŸ’Ž It allows to write expressive code to authorize, validate and authenticate.


Laravel AnyPass

πŸ’Ž A minimal package that helps you login with any password on local environments.


Laravel Widgetize

πŸ’Ž A minimal yet powerful package to give a better structure and caching opportunity for your laravel apps.


Laravel MasterPass

πŸ’Ž A simple package that lets you easily impersonate your users.


⭐️ Your Stars Make Us Do More ⭐️

As always if you found this package useful and you want to encourage us to maintain and work on it, Please press the star button to declare your willingness.


I believe in standardizing automobiles. I do not believe in standardizing human beings.

"Albert Einstein"

More Repositories

1

laravel-microscope

Fearless refactoring, it does a lot of smart checks to find certain errors.
PHP
1,307
star
2

laravel-widgetize

A minimal package to help you make your laravel application cleaner and faster.
PHP
902
star
3

laravel-heyman

Declarative style of authorization and validation in laravel.
PHP
880
star
4

laravel-MasterPass

Helps you securely setup a master password and login into user accounts with it.
PHP
354
star
5

laravel-video

A laravel package to stream video content.
PHP
232
star
6

laravel-anypass

A minimal package that helps you login with any password on local environments
PHP
211
star
7

eloquent-relativity

Allows you to decouple your eloquent models from one another.
PHP
147
star
8

laravel-decorator

Easily decorate your method calls with laravel-decorator package
PHP
129
star
9

eloquent-mockery

Mock your eloquent queries without the repository pattern
PHP
123
star
10

laravel-middlewarize

Use middleware to decorate method calls within your application code.
PHP
105
star
11

laravel-nullable

Functional programming paradigms in laravel to avoid run-time errors.
PHP
102
star
12

laravel-temp-tag

Temporarily and Transparently, tag your eloquent models
PHP
100
star
13

laravel-smart-facades

Strategy design pattern in laravel, the easiest way.
PHP
87
star
14

laravel-password-history

Keep a password history of your users to prevent them from reusing the same password.
PHP
64
star
15

iranian-laravel-contributors

The list of people from Iran who have contributed to the laravel framework
40
star
16

laravel-tokenized-login

Two factor authentication in Laravel
PHP
36
star
17

eloquent-history

PHP
31
star
18

smart-realtime-facades

PHP
30
star
19

php-smart-search-replace

Smart search/replace functionality for PHP code
PHP
27
star
20

gilded_rose

Based on a tutorial for code refactoring
PHP
25
star
21

why_github_is_not_open_source

Why github.com is NOT open-source???
25
star
22

laravel-makesure

Readable syntax to write tests in laravel
PHP
21
star
23

eloquent-rating

5 star rating for eloquent models
PHP
18
star
24

laravel-file-cache-cleaner

Delete the obsolete cache files in the storage directory
PHP
15
star
25

php_token_analyzer

PHP
11
star
26

laravel-db-freeze

A package that allows you to bypass any insert, edit, delete into your database in demo mode through .env variables
10
star
27

laravel-anytoken

A minimal development package that helps you fake any api token as a valid one during development
10
star
28

composer-json

A utility class for read composer.json data and use it in PHP
PHP
9
star
29

crudbooster-statistics

Statistics module for Crudbooster CMS
HTML
8
star
30

imanghafoori1

8
star
31

laravel-microscope-ui

8
star
32

laravel-tik8

Simple modular ticketing system by laravel
8
star
33

crudbooster-logs

A plug-in for CrudBooster CMS to add log functionality
PHP
8
star
34

chat

node chat application
HTML
7
star
35

crudbooster-notifications

Adds notification functionality to crudbooster
PHP
7
star
36

laravel-nice-middlewares

This is a plug-in for laravel-middlewarize package.
PHP
6
star
37

questionist

Advanced event/listener system
PHP
6
star
38

abstract_php_filesystem

PHP
3
star
39

example_query

PHP
2
star
40

test-bin

1
star
41

laravel-endpoints

Define your endpoints as classes
1
star
42

git_toturial

1
star