How should I organize my controllers using the SOLID methodology?

5

I am currently developing a project and have noticed that my controllers are responsible for more than one activity.

Thinking about it, I remembered the Laravel Brazil Community Hangout on SOLID.

The problem is that I do not know the best way to organize the controllers ... currently there is the following case:

ContactsController

Responsibilities:

  • CRUD (index, create, store, edit, update, destroy)
  • Import contacts from another system via CSV (3 methods)
  • Export contacts to other systems in CSV (2 methods)
  • Find contacts through simple or complex filters (2 methods)

How should I organize this controller?

  • ContactsController
  • ContactImportsController or ContactsIOController
  • ContactsFiltersController

Something like this?

If something is not well explained or you need more details, let me know.

Thank you in advance.

    
asked by anonymous 23.06.2014 / 15:15

1 answer

5

There is a very subtle difference in the Principle of Single Responsibility that causes this kind of questioning.

Having a Single Responsibility is not restricting the object to doing a single thing, but preventing it from doing things from the closest context that you intend to initiate coding.

In your case, you have the context for Contacts . A Controller of Contacts should control everything about Contacts and only.

But controlling everything about Contacts includes everything you've cited, from CRUD to search and import / export.

However, it will not be the Controller itself that will import / export the data, for example. It will only interpose the user's choice made in the GUI (View) and the final product through the Requisition made.

In this case, the Importer is a separate yes class, but it is not a Controller , at least not in the conventional definition, as an Importer should be able to import not just contacts , but any kind of information.

I'm not sure if methods really referred you to class methods or import methods, but assuming you have been importing ways, this is not even the responsibility of the Importer itself.

The Importer in turn provide an interface (not visual, of course) for the Controller to work, but the actual import routines, or better the strategies of input analysis (CSV, TXT, XML ...) are other classes, all under a regulatory interface.

In code, we'd have something like this:

class ContactsController {

    // GUIs

    public function index() {}
    public function create() {}
    public function edit() {}
    public function delete() {}

    public function search() {}

    public function import() {}
    public function export() {}

    // Actions

    public function indexAction() {}
    public function createAction() {}
    public function editAction() {}
    public function deleteAction() {}

    public function searchAction() {}

    public function importAction() {

        $file   = ( isset( $_POST['file'] ) ? $_POST['file'] : NULL );
        $method = ( isset( $_POST['method'] ) ? $_POST['method'] : 'CSV' );

        try {

            $importer = new Importer( $file, $method );

            $data = $importer -> import();

            // Do something with $data

        } catch( ImporterException $e ) {

            die( $e -> getMessage() );
        }
    }

    public function exportAction() {}
}

class Importer {

    private $strategy;

    public function __construct( $strategy, $file ) {

        $strategyClass = sprintf( '%sStrategy.php', $strategy );

        if( ! class_exists( $strategyClass ) {

            throw new ImporterException(

                sprintf( 'Importer Strategy %s does not exist', $strategy )
            );
        }

        $this -> strategy = new $strategyClass( $file );
    }

    public function import() {
        return $this -> strategy -> import();
    }
}

class CSVStrategy implements ImporterInterface {

    private $file;

    public function __construct( $file ) {

        $this -> file =& $file;
    }

    public function import() {

        // Do something with $this -> file and return
    }
}

interface ImporterInterface{

    public function import();
}
  

The code fragment above is for educational purposes only and therefore not properly tested or even optimized, and may even contain syntax errors

    
23.06.2014 / 16:13