Model

Represents the persistent classes of the system. For example A Book class. We will use a library management system to explain this part.
In your application, create a “model” folder which will contain your classes. For each class, you must create its definition.

1. Notion of “model definition”

a. Sample code

<?php
namespace myapp\model;
use muuska\constants\DataType;
use muuska\constants\FieldNature;
use muuska\model\AbstractModelDefinition;
use myapp\option\AccessibilityProvider;
class LibraryDefinition extends AbstractModelDefinition
{
    protected static $instance;
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new static();
        }
        return self::$instance;
    }
    protected function createDefinition()
    {
        return array(
            'name' => 'library',
            'primary' => 'id',
            'autoIncrement' => true,
            'fields' => array(
                'addressId' => array(
                    'type' => DataType::TYPE_INT,
                    'nature' => FieldNature::EXISTING_MODEL_ID,
                    'required' => true,
                    'reference' => AddressDefinition::getInstance()
                ),
                'name' => array(
                    'type' => DataType::TYPE_STRING,
                    'nature' => FieldNature::NAME, 'maxSize' => 200
                ),
                'openingTime' => array(
                    'type' => DataType::TYPE_STRING,
                    'validateRule' => 'isGenericName'
                ),
                'accessibility' => array(
                    'type' => DataType::TYPE_INT,
                    'nature' => FieldNature::OPTION,
                    'optionProvider' => new AccessibilityProvider()
                ),
                'image' => array(
                    'type' => DataType::TYPE_STRING,
                    'nature' => FieldNature::IMAGE
                ),
                'description' => array(
                    'type' => DataType::TYPE_STRING,
                    'nature' => FieldNature::LONG_TEXT
                )
            )
        );
    }
    public function createModel()
    {
        return new Library();
    }
}

 

b. Getting a single instance

The following block of code should be added in all of your definitions to ensure that there is only one instance of the class in the application.

 

protected static $instance;
public static function getInstance()
{
    if (self::$instance === null) {
        self::$instance = new static();
    }
    return self::$instance;
}

 

c. the “createModel” method

The “createModel” method allows to create a new instance of a model.

 

d. The “createDefinition” method

The “createDefinition” method returns an associative table defining the class. The following content explains these different fields.

–  FIELDS

  • name
    1. Description : Represents the name of the class that will be used by the persistence support (For example for the database it will represent the name of the table).
    2. Data type : string
    3. optional : no
  • primary 
    1. Description : The name of the field representing the object identifier.
    2. Data type : string
    3. optional : yes (If the object has more than one identifier)
  • primaries
    1. Description : Nom des champs permettant d’identifier l’objet.
    2. Data type : table
    3. optional :  yes (If the object has only one identifier)
  • autoIncrement 
    1. Description : Indicates whether you want the value of the identifier of an object to be assigned automatically. This field is only useful for Classes with a single identifier.
    2. Data type : boolean
    3. optional : yes
  • multilingual
    1. Description : Allows to specify whether the object supports multilingual.
    2. Data type : boolean
    3. optional : yes
  • uniques
    1. Description : allows to specify an attribute group for which you do not want to have multiple records with the same pairs of values. For example, if I don’t want to have two authors with the same first name and the same last name
      'uniques' => array(
            array('firstName', 'lastName')
       ),
    2. Data type : Associative table
    3. optional : yes
  • fields
    1. Description : Represents the definition of persistent attributes of the class.
    2. Data type : Associative table
    3. optional : no
  • presentationFields 
    1. Description : Fields used for the description of the object and also for auto-completion.
      If you leave this information blank, the system will search for the appropriate fields among the following values (in this order): “firstName + lastName“,“Label”, “displayName”, “name”, “title”, “code”, “reference”.
    2. Data type : table
    3. optional : yes
  •  presentationFields Separator 
    1. Description : The separator to be used in case “presentationFields” contains more than one element. The not-fault value is space.
    2. Data type : string
    3. optional : yes
  • projectType 
    1. Description : The type of project to which the object is associated. The default is \muuska\project\constants\ProjectType::Application
    2. Data type :  string
    3. optional : yes
  • projectName 
    1. Description : The name of the project to which the object is associated.
    2. Data type : string
    3. optional : yes
  • associations 
    1. Description : It is associative table where the key represents the name of the association and the value is a table defining the association. The definition of the association is an associative table with the following info:
      • reference : object representing the definition of the external class.
      • field : field name representing the ID of the common object in the external class
    2. Data type : table
    3. optional : yes

e. Definition of multiple associations

'associations' => array(
    'books' => array(
        'reference' => BookDefinition::getInstance(),
        'field' => 'libraryId'
    ),
    'speciatilies' => array(
        'reference' => LibrarySpecialityDefinition::getInstance(),
        'field' => 'libraryId'
    ),
    'types' => array(
        'reference' => LibraryTypeDefinition::getInstance(),
        'field' => 'libraryId'
    ),
)

f. Definition of a field

The “fields” field of the model definition returned by the “createDefinition” method

The “fields” field is an associative table representing the definition of the persistent attributes of the class.

NB: If in the “createDefinition” method, the “autoIncrement” field is defined with the value “true“, the property representing the identifier of the class must not appear in the “fields” table.

The “fields” table is of the form “key-value” where:

  • The “key” is a string representing the name of a property of the example class “firstName”.
  • The “value” is an associative table representing the definition of the property. The following table lists the available fields :

– FIELDS

    • type
      1. Description : The type of data handled by the attribute. This type is the type under which the data will be persisted. (integer, character string, boolean, etc …) A set of constants are defined in the muuska \ constants \ DataType class.
      2. Data type : integer
      3. Optional : no
    • nature
      1. Description : Represents the nature of the data. That is, the type of information that the variable is supposed to contain can be a phone number, an email address, a password, a name, etc.
        A set of constants is defined in the muuska \ constants \ FieldNature class.
      2. Data type : integer
      3. Optional : yes
    • required
      1. Description : Allows to specify whether the attribute is required or not.
      2. Data type : boolean
      3. Optional : yes
    • validationRule 
      1. Description : Allows to specify the name of the method that will be used for the validation of attribute data. This name must match a method name in the muuska \ validation \ ValidationRuleManager class.
        Example “isPhoneNumber”
      2. Data type : string
      3. Optional :  yes
    • validationRules
      1. Description : List of validation rules
      2. Data type : table
      3. Optional :  yes
    • maxSize
      1. Description : The maximum number of characters allowed by the attribute.
      2. Data type : integer
      3. Optional : yes
    • minSize
      1. Description : The minimum number of characters allowed by the attribute.
      2. Data type : integer
      3. Optional : yes
    • maxValue
      1. Description : Maximum value allowed.
      2. Data type : integer
      3. Optional : yes
    • minValue
      1. Description : Minimum value allowed.
      2. Data type : integer
      3. Optional : yes
    • unique
      1. Description : Allows specifying that the attribute value should be unique eg I don’t want to have two books in my system with the same code.
      2. Data type : boolean
      3. Optional : yes
    • reference
      1. Description : Required only in case the nature of the field is FieldNature :: EXISTING_MODEL_ID. It represents the object containing the definition of the class to which the attribute points.
        This object must be an instance of the class
        “Muuska \ model \ AbstractModelDefinition”.
      2. Data type : Object
      3. Optional : yes
    • onDelete
      1. Description : Required only if the nature of the field is FieldNature :: EXISTING_MODEL_ID. Allows to specify the behavior (CASCADE, RESTRICT, etc …) in case of deletion of the object reference.
        A set of constants are defined in the muuska\dao\constants\ReferenceOption class.
      2. Data type : integer
      3. Optional : yes
    • optionProvider
      1. Description : Required only if the nature of the field is FieldNature :: OPTION.
        Represents the object that will provide the options to use.
        This attribute is useful in case we have a predefined list of values for a field in other words, it represents enumerations.
        This object must be an instance of the “muuska\option\AbstractOptionProvider” class.
      2. Data type : Object
      3. Optional : yes
    • validator
      1. Description : Allows to specify a custom validator for the field.
        This object must implement the \muuska\validation\Validator interface.
        The \muuska\validation\DefaultValidator class
        Implements this interface and allows you to create an instance by defining a callback function.
      2. Data type : Object
      3. Optional : yes

Implementing a custom validator for a field

$callback = function (\muuska\validation\input\ValidationInput $input) {
    $result = null;
    if ($input->getValue() === 'My value') {
        $result = App::validations()->createDefaultValidationResult(true);
    } else {
        $result = App::validations()->createDefaultValidationResult(
            false, 
            [App::translateApp(App::createErrorTranslationConfig(),'My value is required',$input->getLang())]
        );
    }
    return $result;
};
$validator = App::validations()->createDefaultValidator($callback);

'description' => array(
    'type' => DataType::TYPE_STRING,
    'nature' => FieldNature::LONG_TEXT,
    'validator' => $validator
)

Create options for a field

Add an options folder in your application then add the class of your option.

<?php
namespace myapp\option;

use muuska\option\provider\AbstractOptionProvider;
use myapp\constants\Accessibility;

class AccessibilityProvider extends AbstractOptionProvider
{
    protected function initOptions()
    {
        $this->addArrayOption(Accessibility::PUBLIC, $this->l('Public'));
        $this->addArrayOption(Accessibility::PRIVATE, $this->l('Private'));
    }
}

The initOptions method allows to initialize your options. With the addArrayOption method, you can add your options, the first parameter represents the value of the option, and the second the label of the option. You must use the “l” method to obtain the translation of a text.

Definition of an external field

'addressId' => array(
    'type' => DataType::TYPE_INT,
    'nature' => FieldNature::EXISTING_MODEL_ID,
    'required' => true,
    'reference' => AddressDefinition::getInstance(),
    'onDelete' => ReferenceOption::CASCADE
),

g. Using an associative table as a template

You don’t have to create a model class for all of your definitions. You can specify in the definition that you want to create an associative table. To do this, you need to add the following statement in the definition:

'modelType' => self::MODEL_TYPE_ARRAY,

Once this instruction has been added, each time we want an instance of the model, an instance of the muuska \ model \ ArrayModel class will be returned.
This class contains the following methods :

getPropertyValue

Get the value of a property. It takes the name of the property as a parameter.

setPropertyValue

Used to change the value of a property, it takes the name of the property and the new value as a parameter.

The final definition of the object will be :

<?php
namespace myapp\model;

use muuska\constants\DataType;
use muuska\constants\FieldNature;
use muuska\dao\constants\ReferenceOption;
use muuska\model\AbstractModelDefinition;

class  CategoryDefinition extends AbstractModelDefinition
{

    protected static $instance;

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new static();
        }
        return self::$instance;
    }

    protected function createDefinition()
    {
        return array(
            'name' => 'category',
            'primary' => 'id',
            'autoIncrement' => true,
            'fields' => array(
                'parentId' => array(
                    'type' => DataType::TYPE_INT,
                    'nature' => FieldNature::EXISTING_MODEL_ID,
                    'reference' => static::getInstance(),
                    'onDelete' => ReferenceOption::CASCADE
                ),
                'name' => array(
                    'type' => DataType::TYPE_STRING,
                    'nature' => FieldNature::NAME,
                    'required' => true,
                    'maxSize' => 200
                ),
                'image' => array(
                    'type' => DataType::TYPE_STRING,
                    'nature' => FieldNature::IMAGE,
                    'maxSize' => 50
                ),
                'description' => array(
                    'type' => DataType::TYPE_STRING,
                    'nature' => FieldNature::LONG_TEXT
                )
            )
        );
    }
}

2. Notion of model

It is a class with attributes, a method, getters and setters.
It must inherit from the \muuska\model\AbstractModel class

<?php
namespace myapp\model;

use muuska\model\AbstractModel;

class Library extends AbstractModel{
	protected $id;
	protected $addressId;
	protected $name;
	protected $openingTime;
	protected $accessibility;
	protected $image;
	protected $description;	

	public function getId(){
		return $this->id;
	}
	public function setId($id){
		$this->id = $id;
	}
	public function getAddressId(){
		return $this->addressId;
	}
	public function setAddressId($addressId){
		$this->addressId = $addressId;
	}
	public function getName(){
		return $this->name;
	}
	public function setName($name){
		$this->name = $name;
	}
	public function getOpeningTime(){
		return $this->openingTime;
	}
	public function setOpeningTime($openingTime){
		$this->openingTime = $openingTime;
	}
	public function getAccessibility(){
		return $this->accessibility;
	}
	public function setAccessibility($accessibility){
		$this->accessibility = $accessibility;
	}
	public function getImage(){
		return $this->image;
	}
	public function setImage($image){
		$this->image = $image;
	}
	public function getDescription(){
		return $this->description;
	}
	public function setDescription($description){
		$this->description = $description;
	}
}

The AbstractModel class contains the following methods :

a. setAssociated

Used to modify the instance of an external object. It takes as parameter the name of the field on which the object reference is defined and the instance of the new object.

b. getAssociated

Obtain the instance of an external object. It takes as parameter the name of the field on which the object reference is defined.

c. hasAssociated

Used to check if the instance of an external object exists. It takes as parameter the name of the field on which the object reference is defined.

d. addMultipleAssociated

Allows to add an object to a multiple association. It takes as parameter the name of the multiple association and the object instance.

e. setMultipleAssociatedModels

Allows to modify an instance of a multiple association. It takes as parameter the name of the multiple association and an table of objects.

f. getMultipleAssociatedModels

Used to get instances of a multiple association. It takes the name of the multiple association as a parameter.

3. Model test

We are going to create a “test-model” control to test our Models.

a. Creating the test-model control

Go to your controller/front folder and create a TestModelController class

<?php
namespace myapp\controller\front;

use muuska\controller\AbstractController;

class TestModelController extends AbstractController
{
    protected function processDefault()
    {
        
    }
}

b. Modifying the FrontSubApplication class

<?php
namespace myapp;

use muuska\project\AbstractSubApplication;

class FrontSubApplication extends AbstractSubApplication
{
    public function createController(\muuska\controller\ControllerInput $input) {
        $result = null;
        if ($input->checkName('hello-world')) {
            $result = new \myapp\controller\front\HelloWorldController($input);
        }elseif ($input->checkName('test-model')) {
            $result = new \myapp\controller\front\TestModelController($input);
        }
        return $result;
    }
}

Enter the url http://localhost/muuska/fr/test-model to access it

c. Test code

<?php
namespace myapp\controller\front;

use muuska\controller\AbstractController;
use myapp\model\Library;
use myapp\constants\Accessibility;
use myapp\model\AddressDefinition;
use myapp\model\SpecialityDefinition;

class TestModelController extends AbstractController
{
    protected function processDefault()
    {
        /*Création de la bibliotheque*/
        $library = new Library();
        $library->setName('My library');
        $library->setOpeningTime('Monday');
        $library->setAccessibility(Accessibility::PUBLIC);
        $library->setDescription('My library desc');
        
        /*Création de l'adresse*/
        $address = AddressDefinition::getInstance()->createModel();
        $address->setPropertyValue('address', '4500 NY');
        $address->setPropertyValue('city', 'New york');
        $address->setPropertyValue('state', 'New york');
        $address->setPropertyValue('country', 'US');
        
        /*Création de la specialité*/
        $speciality1 = SpecialityDefinition::getInstance()->createModel();
        $speciality1->setPropertyValue('name', 'Art');
        
        $speciality2 = SpecialityDefinition::getInstance()->createModel();
        $speciality2->setPropertyValue('name', 'Musique');
        
        /*Modification de l'adresse de la bibliotheque*/
        $library->setAssociated('addressId', $address);
        
        /*Ajout des specialités a la bibliotheque*/
        $library->addMultipleAssociated('specialities', $speciality1);
        $library->addMultipleAssociated('specialities', $speciality2);
        
        /*Affichage*/
        var_dump('bibliotheque : ', $library);
        var_dump('adresse : ', $library->getAssociated('addressId'));;
        var_dump('specialités : ', $library->getMultipleAssociatedModels('specialities'));
    }
}

4. Deployment of models

To deploy the models to the data access medium, you must update your application by doing the following:

a. Create the update process

Open the MyApp class and override the createUpgrade method

AbstractApplication.php
<?php
namespace myapp;

use muuska\project\AbstractApplication;
use muuska\util\App;
use muuska\project\constants\SubAppName;

class MyApp extends AbstractApplication
{
    protected function registerMainDAOSources(){
        parent::registerMainDAOSources();
        $this->registerDaoSource(App::daos()->createPDOSourceFromConfiguration());
    }
    
    protected function createSubProject($subAppName){
        if($subAppName === SubAppName::FRONT_OFFICE){
            return new FrontSubApplication($subAppName, $this);
        }
    }
    
    protected function createAppSetup()
    {
        return new \myapp\setup\AppSetup($this);
    }
    
    protected function createUpgrade(){
        
    }
}

Then add the following code :

protected function createUpgrade(){
    $daoInput = App::daos()->createProjectDAOUpgradeInput($this);
    $daoInput->addAddedModelDefinition(SpecialityDefinition::getInstance());
    $daoInput->addAddedModelDefinition(TypeDefinition::getInstance());
    $daoInput->addAddedModelDefinition(AddressDefinition::getInstance());
    $daoInput->addAddedModelDefinition(CategoryDefinition::getInstance());
    $daoInput->addAddedModelDefinition(PublisherDefinition::getInstance());
    $daoInput->addAddedModelDefinition(AuthorDefinition::getInstance());
    $daoInput->addAddedModelDefinition(LibraryDefinition::getInstance());
    $daoInput->addAddedModelDefinition(LibraryTypeDefinition::getInstance());
    $daoInput->addAddedModelDefinition(LibrarySpecialityDefinition::getInstance());
    $daoInput->addAddedModelDefinition(BookDefinition::getInstance());
    return App::projects()->createDefaultProjectUpgrade($this, $this->daoFactory, $daoInput);
}

b. Change the version of the application

You must change the version of the app so that the update can start automatically the next time you run the app. To do this, add the following statement in the MyApp class

protected $version = '1.1';

c. final application code

<?php
namespace myapp;

use muuska\project\AbstractApplication;
use muuska\util\App;
use muuska\project\constants\SubAppName;
use myapp\model\SpecialityDefinition;
use myapp\model\TypeDefinition;
use myapp\model\AddressDefinition;
use myapp\model\CategoryDefinition;
use myapp\model\PublisherDefinition;
use myapp\model\AuthorDefinition;
use myapp\model\LibraryDefinition;
use myapp\model\LibraryTypeDefinition;
use myapp\model\LibrarySpecialityDefinition;
use myapp\model\BookDefinition;

class MyApp extends AbstractApplication
{
    protected $version = '1.1';
    
    protected function registerMainDAOSources(){
        parent::registerMainDAOSources();
        $this->registerDaoSource(App::daos()->createPDOSourceFromConfiguration());
    }
    protected function createSubProject($subAppName){
        if($subAppName === SubAppName::FRONT_OFFICE){
            return new FrontSubApplication($subAppName, $this);
        }
    }
    protected function createAppSetup()
    {
        return new \myapp\setup\AppSetup($this);
    }
    
    protected function createUpgrade(){
        $daoInput = App::daos()->createProjectDAOUpgradeInput($this);
        $daoInput->addAddedModelDefinition(SpecialityDefinition::getInstance());
        $daoInput->addAddedModelDefinition(TypeDefinition::getInstance());
        $daoInput->addAddedModelDefinition(AddressDefinition::getInstance());
        $daoInput->addAddedModelDefinition(CategoryDefinition::getInstance());
        $daoInput->addAddedModelDefinition(PublisherDefinition::getInstance());
        $daoInput->addAddedModelDefinition(AuthorDefinition::getInstance());
        $daoInput->addAddedModelDefinition(LibraryDefinition::getInstance());
        $daoInput->addAddedModelDefinition(LibraryTypeDefinition::getInstance());
        $daoInput->addAddedModelDefinition(LibrarySpecialityDefinition::getInstance());
        $daoInput->addAddedModelDefinition(BookDefinition::getInstance());
        return App::projects()->createDefaultProjectUpgrade($this, $this->daoFactory, $daoInput);
    }
}

d. Start update

Launch any controller in your app. The current data access medium (database) will update your application. To verify, open your database client, you will see your tables added with the prefix “msk_a_“.

database_books

 

Leave a Reply

Your email address will not be published. Required fields are marked *