Storables - The way you store data in Framelix

Storables are a very important core feature of Framelix. With Storables you basically store, modify, update, delete everything in your database. A storable is a PHP class with properties. Each property become a column in the database. The database scheme is automatically managed by the framework, you never need to write queries to update your database structure. Don't worry, the system does nothing what can cause data-loss by default. You always have full control of when and what actually get updated in the database. More on that later. Let's see a very basic Storable here, our SimpleDemo storable.

Copy to clipboard

Let's break it down - The class doc comments

The @property doc comments define which properties a storable have. This is the only place where you need to define the properties to a storable. The framework does extract all @property annotations and parses the type and name of a property. Each default PHP type like string, int, bool, float will be the same column type in the database. A type that can be optional is marked with |null. If it is not optional, you cannot store it with a value of NULL.

As you see in this example, we have some special properties. mixed|null $anyJsonData simple can hold any data and will be longtext in the database. The data is automatically converted to a JSON string in the database, so you can run JSON database functions on that one.

DateTime|null $lastLogin hold a DATETIME in the database. You can make own individual classes to be valid as a property. More on the bellow.

SimpleDemoEntry|null $referenceEntry hold a reference to another Storable in the database. In the database this will be a BIGINT, as it only holds the ID of the given storable. The framework resolves this to the required Storable when you need it to fetch.

SimpleDemoEntry|null $referenceEntry hold a reference to another Storable in the database. In the database this will be a BIGINT, as it only holds the ID of the given storable. The framework resolves this to the required Storable when you need it to fetch.

Arrays and other data is supported with the mixed datatype, as it is converted to a JSON string in the database. Defining any @property as an array, by adding square brackets to the type, example string[], will result in an error. This is not supported.

If your intention of using an array is to store 1-n data entries to a Storable (Let's say a list of strings), than it is best practice to create another Storable type with a @property ParentStorableType $parent reference back to the parent. This is future-proof and you can always extend this child Storable with more data properties. A good example of that is the built-in User and Roles with Framelix/src/Storable/User.php and Framelix/src/Storable/UserRole.php

IDs

Each saved storable has a unique id across ALL storables in the database. This is done by only one table has an auto_increment id, which is on framelix__id. At the time a storable is inserted the first time, an entry in framelix__id is created which generates a new unique. This id is then used in the storables own table as primary key on column id. So the result is, if you only have an id and know nothing else, you can find out the exact entry from the database, even when you don't know the storable type, because this id is gueranteed to not be used for another storable.

Inheritance and make a class a storable

To make a storable, you have to extend your class at least from Storable or StorableExtended. Storable just provides all features for a storable, with one property, id. StorableExtended includes 4 more properties which automatically stores createTime, updateTime, createUser, updateUser on the corresponding store action. This is recommended for almost all Storables, when you ever want to know the create and update infos.

Make any class a storable property

Framelix uses the interface StorablePropertyInterface to make PHP class an allowed storable property, like the built-in DateTime, Date and Time. This interface requires you to set proper information of how the PHP class get stored and fetched from the database. It's similar to the process of serialize and unserialize.

Copy to clipboard

Override of functionality and defination with setupStorableSchema

The line protected static function setupStorableSchema(StorableSchema $selfStorableSchema) in the SimpleDemoEntry is optional, but you can use it to provide a lot more details to the behaviour in the database. As you can see here, we have used it to override a properties db column type and added an index to a column. To find out which options you have, just use the auto-completion feature of your IDE, it gives you all the information you need for coding.

Your first entry - Store, Update, Delete

So we have talked a lot about how the structure is defined. Let's create your first entry.
Copy to clipboard

As you can see here, the code is straight forward. Every property and method is fully autocompleted by your IDE.

Fetching storables

Once you have created storables in the database, you need some to fetch the things later on. For this there are a handful of usefull methods to use.

Fetch with MyStorable::getById[s], MyStorable::getByCondition[One]

This are the functions you need and will use the most when fetching storables from the database. Let's see some code.

Copy to clipboard

It will be redundant to explain you the functions, because the code documentation on the class itself is very good to get you in-depth information. But let's explain some specials of getByCondition

The first parameter is basically comparable to a default SQL WHERE condition. The placeholders in brackets {xxx} will be replaced with the corresponding keys in the second parameter $parameters.
All placeholders are automatically proper sql escaped, so you can pass direct user input with need to worry about SQL injections.

Depth-Joins is a special feature in the condition part of getByCondition. If you have referenced storables, as for example by default there is a createUser property, which itself is another Storable called User. This User have other properties, and so on. Now, with depth-joins, you can write a condition intuitively like a path to the property you want to compare. So lets see an example. This is all handled by the framework and generate required joins automatically for you in the background. No need to do any join by yourself.

Copy to clipboard

This example will fetch a SimpleDemoEntry, where it's referenced property createUser will have a User referenced, where it's property is flagLocked = true

Fetching when you know only ID but not the type

There can be a point, where you only have an ID from a Storable, but you don't know the Storables class. No problem. You can can getById even from the top-most abstract Storable method.

Copy to clipboard

Storable Caching

Framelix make use of a clever caching mechanism, to try you to save a lot of queries to the database. Basically, whenever you use getById fetch, the framework will check if the Storable already have been fetched in the past and return the same instance that already exist.
This procedure is internaly used as often as possible. For example when fetching property references.
Let's say you have 100 Storables, each have the same createUser attached. You can call ->createUser on each of this 100 storable individually, but there is only one query for the database. All other 99 calls make use of the internal framework Storable cache. Pretty cool, right?