2
votes

With the separation of admin and non admin at the controller level (which I much prefer to using 1.3's admin prefix) I am finding I'm duplicating too much code. Is there a way to share controller logic between the two controllers? I don't feel what I want to share should sit in a component as it's specific to the controller.

Example:

Table: Environments (contains server details)

non admin and admin can both restart a their/the customers environments.

This function uses a "Provision" component to call a web service that provisions the request returning a Job ID which I save against each environment. I have as much code in the Table class and Entity class as I can but I still have the following function in both non admin and admin controllers with the only difference being:

$this->currentUser->customer_id

being changed for

$this->currentCustomerId

in the admin function.

public function restart($prodtype)
  {
    $environments = $this->Environments->getEnvironments( $this->currentUser->customer_id, $prodtype, 2, [ 
        'Customers' 
    ] );
    if(! $environments)
    {
      Log::error( DatabaseLog::formatErrorMessage( $this->name, $this->request->params['action'], 197, "getEnvironments returned no environments" ) );
      $this->Flash->error( "Uh Oh! Sorry we were unable to restart your environments. Please try again" );
      $this->redirect( $this->referer() );
    }
    $this->loadComponent( 'Provision' );
    $jobId = $this->Provision->restartCustomerEnvironment( $environments[0] );
    if(! $this->Environments->saveJobId( $environments, $jobId ))
    {
      Log::error( DatabaseLog::formatErrorMessage( $this->name, $this->here, 210, "Error saving environments " . print_r( $environments ) ) );
    }
    $this->Flash->success( "Great! Your {$environments[0]->full_production_name} environments are being restarted this may take a few moments" );
    $this->redirect( $this->referer() );
  }

I feel like I need shared controller:

  • App\Controller\EnvironmentsSharedController extends AppController
  • App\Controller\Admin\EnvironmentsController extends EnvironmentsSharedController
  • App\Controller\EnvironmentsController extends EnvironmentsSharedController
2

2 Answers

2
votes

I am finding I'm duplicating too much code. Is there a way to share controller logic between the two controllers? I don't feel what I want to share should sit in a component as it's specific to the controller.

Well, if it's not generic for you taste to share it through a component, then you have two, maybe three options left:

  1. Use phps traits
  2. Extend the controller using a base class (MyFancyBaseController.php)
  3. Use FoC's CRUD plugin to share whole actions between controllers and use events to control them
2
votes

After testing with components and base classes, I got both solutions working. I feel the better solution to my needs is having a base class. I think sometimes I rely too much on a framework for the solution rather than going back to basics and OOP. It would be nice if the cakePHP docs gave some guidance to how to layout base classes and show this as a possibility within controllers.

  • I created a new directory/namespace Controllers/Base/
  • I then created an Environments Base Controller:

    namespace App\Controller\Base;
    
    use App\Controller\AppController;
    use App\Model\Entity\Environment;
    use Cake\Log\Log;
    
    /**
     * Environments Controller
     *
     * @property \App\Model\Table\EnvironmentsTable $Environments
     */
    class EnvironmentsBaseController extends AppController
    {
    }
    
  • I then extended each non-admin/admin

    class EnvironmentsController extends EnvironmentsBaseController
    {
    }