<?php
/**
 * Abstract CRUD controller
 * @author Jérôme Brilland
 * @version 4.1
 * @copyright (c) 2012-2022, Jérôme Brilland
 * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License
 * @abstract
 */
abstract class CRUDController extends ActionController {

    /**
     * @var string Model class name
     * @access protected
     */
    protected $modelClassName;
    
    /**
     * @var int
     * @access protected
     */
    protected $itemsPerPage = 30;

    /**
     * @var array 
     * @access protected
     */
    protected $where = [];

    /**
     * @var string|array
     * @access protected
     */
    protected $columns = '*';

    /**
     * @var array
     * @access protected
     */
    protected $join = null;

    /**
     * Index action
     * @access public
     */
    public function doIndex() {
        $this->doList();
    }

    /**
     * Retrieves and displays the list of objects
     * @access public
     */
    public function doList() {
        $totalItems = $this->modelClassName::count( $this->where );
        $currentPage = $this->request->getParameters()->getInt( 'page', $this->request->getAttribute( 'page', 1 ) );
        if ( !\Validator::validateInteger( $currentPage ) ) {
            $this->forward( 'controllers\\ErrorController', '400' );
        }
        $this->paginator = new Paginator( $this->itemsPerPage, $totalItems, $currentPage );
        $this->where['LIMIT'] = [$this->paginator->getOffset(), $this->paginator->getLimit()];
        if ( $this->request->hasParameter( 'order' ) ) {
            $this->where['ORDER'] = $this->request->getParameters()->getWord( 'order' );
        }
        if ( !empty( $this->join ) ) {
            $this->models = $this->modelClassName::findMany( $this->join, $this->columns, $this->where );
        } else {
            $this->models = $this->modelClassName::findMany( $this->columns, $this->where );
        }
    }

    /**
     * Displays the add form
     * @access public
     */
    public function doAddform() {
        $modelName = $this->getModelName();
        if ( !isset( $this->model ) ) {
            $this->model = new $this->modelClassName();
        }
        $this->$modelName = $this->model;
    }

    /**
     * Displays the update form
     * @access public
     */
    public function doUpdateform() {
        if ( !isset( $this->model ) ) {
            $this->model = $this->modelClassName::findOne( $this->request->getAttribute( 'id' ) );
        }
        $modelName = $this->getModelName();
        $this->$modelName = $this->model;
    }

    /**
     * Displays the delete form
     * @access public
     */
    public function doDeleteform() {
        $modelClassName = $this->getModelClassName();
        $this->model = $this->modelClassName::findOne( $this->request->getAttribute( 'id' ) );
        $modelName = $this->getModelName();
        $this->$modelName = $this->model;
    }

    /**
     * Inserts a single row
     * @access public
     */
    public function doAdd() {
        $modelName = $this->getModelName();
        $this->model = new $this->modelClassName( $this->request->getParameters()->getArray( $modelName ) );
        $this->beforeAdd();
        if ( !$this->model->save() ) {
            $this->errors = $this->model->getErrors();
            $this->doAddform();
            $this->render( 'addform.html' );
        } else {
            $this->flash['success'] = IoC::resolve( 'i18n' )->__( $modelName . '_successfully_created' );
            $this->afterAdd();
            $this->doList();
            $this->render( 'list.html' );
        }
    }

    /**
     * Updates a single row
     * @access public
     */
    public function doUpdate() {
        $modelName = $this->getModelName();
        $this->model = $this->modelClassName::findOne( $this->request->getAttribute( 'id' ) );
        foreach ( $this->request->getParameters()->getArray( $modelName ) as $key => $value ) {
            $this->model->$key = $value;
        }
        $this->beforeUpdate();
        if ( !$this->model->save() ) {
            $this->errors = $this->model->getErrors();
            $this->doUpdateform();
            $this->render( 'updateform.html' );
        } else {
            $this->flash['success'] = IoC::resolve( 'i18n' )->__( $modelName . '_successfully_updated' );
            $this->afterUpdate();
            $this->doList();
            $this->render( 'list.html' );
        }
    }

    /**
     * Deletes a single row
     * @access public
     */
    public function doDelete() {
        $modelName = $this->getModelName();
        $this->model = $this->modelClassName::findOne( $this->request->getAttribute( 'id' ) );
        $this->beforeDelete();
        if ( !$this->model->delete() ) {
            $this->errors = $this->model->getErrors();
            $this->doDeleteform();
            $this->render( 'deleteform.html' );
        } else {
            $this->flash['success'] = IoC::resolve( 'i18n' )->__( $modelName . '_successfully_deleted' );
            $this->afterDelete();      
            $this->doList();
            $this->render( 'list.html' );
        }
    }

    /**
     * @access public
     */
    public function doShow() {
        $modelName = $this->getModelName();
        $this->model = $this->modelClassName::findOne( $this->request->getAttribute( 'id' ) );
        $this->$modelName = $this->model;
    }

    /**
     * @return string The model name
     * @access public
     */
    public function getModelName() {
        $class = new ReflectionClass( $this->modelClassName );
        return strtolower( $class->getShortName() );
    }
    
    /**
     * Executed before the Add action is called
     * @access protected
     */
    protected function beforeAdd() {
        
    }

    /**
     * Executed after the Add action is called
     * @access protected
     */
    protected function afterAdd() {

    }

    /**
     * Executed before the Update action is called
     * @access protected
     */
    protected function beforeUpdate() {
        
    }

    /**
     * Executed after the Update action is called
     * @access protected
     */
    protected function afterUpdate() {
        
    }

    /**
     * Executed before the Delete action is called
     * @access protected
     */
    protected function beforeDelete() {
        
    }

    /**
     * Executed after the Delete action is called
     * @access protected
     */
    protected function afterDelete() {
        
    }

}

