Как протолкнуть логику в модель вместо того, чтобы держать ее в контроллере?


Согласно моему пониманию в MVC, логика находится в модели, контроллер обрабатывает координационную часть и представление существует для представления данных.

Ниже приведен извлеченный из контроллера код. Он написан в codeigniter, который является фреймворком PHP. То, что происходит ниже, если изображение доступно, оно будет удалено, иначе пользователь будет перенаправлен на удаление формы изображения.
if($this->records_model->check_img_availability($img_data))
{   
    //delete if the image is available
    $this->records_model->delete_img($img_data); 
}
else
{
    //redirected to delete image form
    $this->session->set_flashdata('errormsg', 'Img is not available'); 
    redirect(base_url() . 'records/deleteimg/');   
}

Согласно MVC выше неверно, так как логика находится в контроллер. Он проверяет, доступно ли изображение, в зависимости от того, что изображение будет удалено или перенаправлено пользователем.

Как протолкнуть логику в модель вместо того, чтобы держать ее в контроллере?

1 2

1 ответ:

Используйте слой сервиса. Логика, которую вы теперь имеете в своих контроллерах, входит в одну из ваших служб.

Служба обычно имеет дело с большими кусками связанной прикладной логики.

Например, если ваше приложение продает продукты, функциональность / логика корзины покупок и клиентской проверки должны были бы куда-то пойти. Поскольку функциональные возможности схожи, вы бы поместили всю связанную логику в класс Services\Shopping, и он мог бы иметь API, подобный следующее:

interface Shopping {

    /**
     * @param int $productId
     * @param int $quantity
     * @return bool
     */
    public function addToCart($productId, $quantity);

    /**
     * @param int $productId
     * @return bool
     */
    public function removeItem($productId);

    /**
     * @param int $productId
     * @param int $quantity
     * @return bool
     */
    public function updateQuantity($productId, $quantity);

    /**
     * Customer checkout.
     *
     * @param string $firstName
     * @param string $lastName
     * @param string $emailAddress
     * @return bool
     */
    public function checkout($firstName, $lastName, $emailAddress, .....etc);

}

В основном вы просто группируете функциональность аналогичного слоя модели в сервисы и даете сервисам хорошее название, которое в общих чертах объясняет, какую работу/функциональность он выполняет.

Теперь ваши контроллеры просто извлекают данные из запроса и отправляют их в службу для выполнения работы.

Некоторый метод контроллера для добавления товара в корзину:

$productId = (int)$this->request->post('productId');
$quantity = (int)$this->request->post('quantity');

// Now send the data to the shopping service to do the work.
$this->shopping->addToCart($productId, $quantity);
Теперь ваши контроллеры могут быть тощими и свободными от логики. Как вы реализуете обслуживание зависит от вас. Сервисы часто могут зависеть от других сервисов для взаимодействия с различными функциональными возможностями слоя модели.

Edit

Для вашего сценария я не могу придумать описательное имя для службы, потому что я не знаю достаточно информации о том, почему вы удаляете изображение, с чем связано изображение и т. д. Поэтому я просто назову его Services\Image. Это может считаться плохим именем для службы, поскольку она слишком специфична, но это только для демонстрации цели.

class Image extends AbstractService {

    /**
      * This is assuming you have an ID for a row in the database containing data
      * about the image like the path to the image so it can be deleted from the server.
      *
      * @param int $id
      * @return bool
      */
    public function delete($id) {

        $mapper = $this->mapperFactory->build('Image');
        $image = $mapper->fetch($id);

        if( $image === null ) {
            return $this->fail('Image does not exist.');
        }

        try {
            $mapper->delete($image); // Delete the image row in the database.
            unlink( $image->getPath() ); // Delete the image file from the server.

            return $this->success('Image deleted successfully.');
        }
        catch(\RuntimeException $ex) {
            return $this->fail('Failed to delete the image.');
        }
    }

}

Методы success() и fail находятся в родительской службе. Они обновляют состояние слоя модели и устанавливают сообщение, чтобы показать пользователю, если это необходимо. Метод failed() исходит от родительского AbstractService, который возвращает true, если вы вызвали $this->fail() в любом дочернем сервисе.

Тогда в представлении вы можете иметь что-то вроде:

if( $this->imageService->hasState() ) {
    if( $this->imageService->failed() ) {
        // If deleting the image failed do whatever you want here.
        // Switch templates, redirect or whatever.
    }
    else {
        // The image was deleted successfully so do whatever you want here.
        // Again, switch templates, redirect or whatever. 
    }
    // Bind the message to the template.
}
Как вы можете видеть, вид имеет мозги и может выполнять логику. Это та область, где CI плох. Представления в основном являются шаблонами без мозги, поэтому логика, которая должна быть в представлении, выталкивается в контроллер, уровень обслуживания и т. д. В основном контроллер связывается с сервисом изображений с помощью метода delete($id), который пытается удалить изображение и устанавливает состояние слоя модели, затем представление обращается к сервису изображений и спрашивает его, в каком состоянии он находится, и продолжает делать все, что ему нужно, основываясь на состоянии слоя модели.

На мой взгляд услуги в целом не следует ничего знать об ответе или о том, как отправить редирект, это ответственность views.

Symfony, вероятно, лучший фреймворк PHP, но многие говорят, что он имеет одну из самых крутых кривых обучения. Я сделал это на нескольких страницах в документации, пока не начал видеть вещи, с которыми я не был согласен, поэтому я решил прекратить исследовать его. Создатель PHP, Расмус Лердорф, даже сам говорит, что все нынешние фреймворки PHP ужасны.

Лучший из удача.