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