CRUD

CREATE READ UPDATE DELETE

Interacting with persistent data (usually through a database) is the likely the most common task for any backend. To simplify this process as far as possible, model interactions make use of a abstraction level called "transformer". This layer uses the model definition of your migration in order to generate database transactions. On this page, we will assume the use of the default MySQL providers.

Magic methods


        public static function __callStatic($method, $args)
        {
            if(!method_exists(self::class, $method)){
                $transform = new Transform('user', self::$db);
                return $transform->$method(...$args);
            } else {
                return self::$method(...$args);
            }
        }
    

When generating a model via the cli-tool, the methods __callStatic and init are generated. While the init function is used to assign providers (usually database injection), __callStatic uses the transformer if no own method is implemented. The transformer handles the following methods:

get(string $id)
Retrieve (read) a entity by primary key (id).

create(array $model)
Create a new entity validated against the migration. This method returns the created entity.

find(array $condition)
Retrieve multiple results based on a condition array (per default in accordance with the DatabaseWrapper).

update(array $model)
Update an existing entity after modification. This method returns the updated entity.

delete(string $id, $hard = false)
Sets a delete stamp or completely removes an entity from the database (when $hard is set to true).

Manipulating behavior

In some instances we might want to manipulate what is retrieved from or goes into the transformer logic. In the following example, let's assume we are dealing with a user model and do not want to retrieve the password.


        public static function __callStatic($method, $args)
        {
            if(!method_exists(self::class, $method)){
                $transform = new Transform('user', self::$db);
                // we are calling our own function "out"
                return self::out($transform->$method(...$args));
            } else {
                return self::$method(...$args);
            }
        }

        private static function out($result)
        {
            // We have to account for two scenarios: a single entry is retrieved
            // or multiple entries are retrieved
            if(isset($result['id'])){
                // single (e.g. via get())
                // unset the password
                unset($result['password']);
            } else {
                // multiple entries
                foreach ($result as $i => $hit){
                    // unset each password
                    unset($result[$i]['password']);
                }
            }
            return $result;
        }
    

Using models in controllers

Since the introduction of providers, decoupling will allow easy testing. Although you can use & call the models in various ways, we here assume utilization of the core's loadModel functionality.

PHP7.4


        // in a Unicore scenario
        use Neoan3\Model\User\UserModel;
        use Neoan3\Core\Unicore;

        class MyRouteController extends Unicore
        {
            function init()
            {
                $this
                    ->uni('demo')
                    ->addRenderParameter('users', function(\Neoan3\Core\Serve $serve) {
                        return $serve->loadModel(UserModel::class)::find(['name' => 'amadeus']);
                    })
                    ->hook('main', 'profile')
                    ->output();

        }

    

        // when extending a frame (e.g. default API behavior)
        use Neoan3\Model\User\UserModel;
        use Neoan3\Frame\Demo;

        class MyRouteController extends Demo
        {
            function getMyRoute()
            {
                return $this->loadModel(UserModel::class)::find(['name' => 'amadeus']);
            }
        }
    

PHP8


        // using PHP8? Attributes can be used to initialize a model
        use Neoan3\Model\User\UserModel;
        use Neoan3\Frame\Demo;

        class MyRouteController extends Demo
        {
            #[InitModel(UserModel::class)]
            function getMyRoute()
            {
                return UserModel::find(['name' => 'amadeus']);
            }
        }
    

Using a wrapper


        // coming from doctrine or laravel and/or want to use objects?
        use Neoan3\Model\User\UserModel;
        use Neoan3\Model\User\UserModelWrapper;
        use Neoan3\Frame\Demo;

        class MyRouteController extends Demo
        {
            #[InitModel(UserModel::class)]
            function getMyRoute()
            {
                $collection =  UserModelWrapper::retrieveMany(['name' => 'amadeus']);
                return $collection->toArray();
            }

            #[InitModel(UserModel::class)]
            function postMyRoute(array $postBody)
            {
                $newUser = new UserModelWrapper();
                $newUser->setName($postBody['name']);
                return $newUser->store()->toArray();
            }

            #[InitModel(UserModel::class)]
            function putMyRoute(array $postBody)
            {
                $existingUser = UserModelWrapper::retrieveOne($postBody['id']);
                $existingUser->setName($postBody['name'])->store();
                return $existingUser->toArray();
            }
        }