Question

According to my understanding in MVC, logic resides in the model, The controller handles the coordination part and the view is there for the data representation.

Below is an extracted code from the controller. It's written in codeigniter which is a PHP framework. What happens below is if the image is available it will be deleted otherwise user will be redirected to delete image form.

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/');   
}

According to MVC above is wrong since the logic resides in the controller. It's checking whether the image is available, depending on that image will be deleted or user redirected.

How to push the logic into model instead of keeping it in the controller?

Was it helpful?

Solution

Use a service layer. The logic you now have in your controllers goes into one of your services.

A service usually deals with large chunks of related application logic.

For example, if your application sold products the functionality/logic of a shopping cart and customer checkout would have to go somewhere. Since the functionalities are similar you would push all that related logic into a Services\Shopping class and it could have an API like the following:

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);

}

You basically just group similar model layer functionality into services and give the services a good name that broadly explains what sort of work/functionality it performs.

Your controllers now just extract the data from the request and send it to a service to do the work.

Some controller method to add an item to the shopping cart:

$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);

Now your controllers can be skinny and free of logic. How you implement the services is up to you. Services can often depend on other services to interface with different functionalities of the model layer.

Edit

For your scenario I cannot come up with a descriptive name for the service because I dont know enough information about why you are deleting the image, what the image is related to etc so I will just call it Services\Image. This could be considered a bad name for a service as it is too specific but it is just for demonstration purposes.

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.');
        }
    }

}

The success() and fail methods are in the parent service. They update the state of the model layer and set a message to show the user if needed. The method failed() comes from the parent AbstractService which will return true if you invoked $this->fail() in any child services.

Then in the view you can have something like:

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.
}

As you can see the view has brains and can perform logic. This is an area where CI is bad. The views are basically templates with no brains, so the logic that should be in the view gets pushed out into the controller, service layer etc.

Basically the controller communicates with the image service via the delete($id) method, that method tries to delete the image and sets the state of the model layer, the view then talks to the image service and asks it what kind of state it is in and proceeds to do whatever logic it needs to do based on the state of the model layer.

In my opinion services generally should not know anything of the response or how to send a redirect, this is the views responsibility.

Symfony is probably the best PHP framework out there but many say it has one of the steepest learning curves. I made it a few pages into the documentation until I started to see things I did not agree with so I decided to stop exploring it.

The creator of PHP, Rasmus Lerdorf, even says himself that all current PHP frameworks are awful.

Best of luck.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top