Blog

Knowledge

ORM system on the example of Doctrine2. Part 1 - Introduction

Do you want learn some about ORM and Doctrine 2 - here you can start

Database in the application

Almost all applications available on the web, from online stores to simple business cards, use widely used relational databases to store their content. And that's great because users who use the services of various applications often have to supply them with some data first. Everyone would agree with the thesis that a store that would require filling in personal data with each purchase to complete the transaction would not be very popular. Thus, the database is a key component of the application being developed.

Database in the application

Current software development standards

What can we say about the application development process itself? Current standards impose on software developers the paradigm of Object-oriented programming (OOP), which was initiated in 1966-1967 by Alan Kay

The combination of the OOP approach with relational databases creates a certain challenge for the developer. Developing an application based on OOP has many advantages in itself, which I will not mention now, but this level of abstraction is 'incompatible' with the data structure in a relational database and difficulties with this incompatibility occur when the application writes or reads data from the database.

 

What to do in that case?

If we have adequate persistence and a lot of time, we can prepare our own mapping mechanism that will 'flatten' the object data skeleton into the form of records (tuples), which allows us to store such data in the database. We would have to do such mapping for all types of objects present in the application and in both directions of communication with the read / write database, which is really a lot of work and a lot of sql queries to write...

 

Fortunately, the ORM (Relationally Mapped Objects) approach comes to the rescue in this situation.

 

'What's going on?' It is an additional layer of abstraction that will take care of all communication with the database for us and will ensure the integrity of the data in it without any special effort. From the programmer's perspective, it's great - we only work with objects that have the 'ability' to record their state in the database. We can call these types of objects Entities because they are already an object-oriented representation of physically existing data.

What ORM is?

But I still don't know what the Doctrine mentioned in the title is

We know what databases are for, we got to know the OOP and ORM approach, so finally the moment has come when we can introduce you to each other.

Dotrine is an ORM implementation - in our PHP world it is the most extensive and the most popular database communication tool.

Doctrine is merely a tool that allows you to perform specific tasks. It is able to do the same as we would be able to do by preparing our own SQL query. The advantage of Doctrin is that it is able to save us a significant amount of work that we would have to put in the preparation of a dedicated ORM system for our application.

A bit about the disadvantages of the ORM

Just as there is no rose without thorns, it is known that there are no perfect solutions for everything and therefore you have to remember about its limitations.

There may be situations in which we will need to build a very complex query to the database containing SQL statements such as JOIN, COUNT (*), HAVING, GROUP BY ORDER BY RAND (), which will not be provided by the built-in mechanisms of associations. In this case, it will be necessary to delve into a special DQL (Doctrine Query Language). This is already a bit of extra work, but I still think it's worth it.

The second negative effect of using Doctine is certainly the performance issue. As long as our database does not store millions of records, we should not experience the negative effects of reduced performance. Unfortunately, the smaller amount of our work is compensated by the increased consumption of server resources.

What is it all about? So practical examples of the use of ORM.

Let's assume that we already have a Product class prepared

class Product{
private $id;
private $name;
private $createdAt;
}

How does Doctrine recognize my object as an entity that should be stored in the database? 

Well, You have to give him a hint and to do this we will use the metadata added to our class.

/** @Entity */
class Product{
private $id;
private $name;
private $createdAt;
}

Now Doctrine knows that the Product class is an ORM object and should have its place in the database, i.e. the table with the same name Of course, if necessary, we can change the target table by adding another annotation '@Table (name = "my_products")'

What is it all about? So practical examples of the use of ORM.

Mapping

The next step is property mapping. The goal is to assign the properties of our class to the appropriate variable types and other parameters. This will allow us to identify the parameters of our entity to the correct columns and give those columns the desired properties. We will solve this problem by adding the annotation / ** @Column (type = "string", length = 75) * / You can see right away that ORM annotations shouldn't be difficult, we just marked a property of our class with a string type with a length of 75 characters.

So we will update all columns:

/**
* @Entity
* @Table(name=”my_products”)
 */
class Product{
	/** @Column(type=”integer”) */
private $id;

/** @Column(type=”string” length=”75”) */
private $name;

/** @Column(type=”datetime”, name=”created_at” */
private $createdAt;
}

This is enough for the Product class object to be saved and retrieved from the database via the Entity Manager (more about that later).

Much more about the properties of annotation.


You have probably noticed that the @Column annotation can take different kinds of parameters, what's more, these parameters can have different values

  • type: optional, defaults to 'string'. Since this parameter is crucial in correct ORM mapping and can define it really, let's do a thorough review of its possible values.
    • string: The object parameter maps to a database column of type VARCHAR
    • integer: The object parameter maps to a database column of type INT and is a common choice for integer values ​​in the range -2147483648 to +2147483647
    • smallint: Will be mapped to the SMALLINT column in the database with the range -32768 to +32767
    • bigint: Also for integer values ​​but very long range and range is -9223372036854775808 to 9223372036854775807
    • boolean: Parameter mapped to a boolean column containing the values ​​True / False / Undefined or the equivalent of TINYINT where three values ​​0/1 / null can be stored
    • decimal: Parameter mapped to a decimal column where we can write decimal numbers consisting of 131072 digits and with a fractional part after the point consisting of 16383 digits
    • date: The field mapped to a column of DATETIME type where the date data is mapped without information about the time and without information about the time zone. If the parameter of your object contains information about the time or the time zone, this information will be omitted when saving to the database.
    • time: The field mapped to a DATETIME column where the time data is mapped. If the object parameter contains information about the date or time zone, they will be ignored when saving to the database.
    • datetime: Field mapped to a DATETIME / TIMESTAMP column. The combination of the two previous types where the Date and time information will be stored in the column, while any possible time zone information will be omitted.
    • datetimetz: The field that maps to the DATETIME / TIMESTAMP column. The widest form of storing information where data about date, time and time zone will be saved.
    • text: The field mapped to the text column with no string max length information.
    • object: Type that maps SQL CLOB to PHP object using serialize () and unserialize ()
    • array: A type that maps SQL CLOB to a PHP array with serialize () and unserialize ()
    • simple_array: A type that maps SQL CLOB to a PHP array using implode () and explode () with a comma as a delimiter. Important - Use only if you are sure the value cannot take the "," sign.
    • json_array: Type that maps from SQL CLOB to PHP array using json_encode () and json decode ().
    • float: Mapping to a Float column (double precision). Important - Only works with local settings that use decimal separators.
    • guid: Mapping a GUID / UUID column to PHP varchar. Defaults to varchar, but can be type specific if the platform supports it.
    • blob: Maps SQL BLOB to PHP resource stream.
  • name: (optionally, defaults to the value of the class property it applies to). It is worth completing this parameter if you want to map a class property to a column with a different name.
  • length: (optional, default is 255) This is the length of the database column (only applicable for columns containing strings.
  • unique: (optional, defaults to FALSE) Use this parameter if the column should be a unique key.
  • nullable: (optional, default value FALSE) Important parameter, decides whether a column in the database can store a value of NULL type.
  • precision: (optional, the default value is 0) Specifies the precision for the decimal number, i.e. it defines the number of digits stored in the column for the entire value. For the examples, precision = 5 will allow us to store values ​​in the range -99999 to 99999.
  • scale: (optional, default 0) Represents the number of digits to the right of the decimal point and cannot be greater than precision. For example, precision = 5, scale = 2 will store values ​​in the range -999.99 to 999.99.
  • columnDefinition: (optional) This parameter should contain DDL SQL (Data Definition Language) and should be the complete column definition. Keep in mind that the "type" attribute will still handle the conversion between PHP values ​​and the database. This field allows the use of advanced relational database features (advanced RMDBS features), but the use of this attribute affects the SchemaTool tool, which will not be able to correctly detect changes for this column. Therefore, use this parameter with caution.
  • options: (optional) An array of additional settings, such as:
    • default: the parameter sets the default value for the column, eg options = {“default”: 0}.
    • unsigned: If set to TRUE then the column can only contain positive numbers such as x> = 0.
    • fixed: a boolean value that indicates whether the indicated string length should be fixed (immutable).
    • comment: comment a column in the schema.
    • collation: this parameter determines the comparison, it affects how the information is sorted and how it is compared, it also determines the rules for sorting, case and accent sensitivity in the database. As a curiosity, I will also mention the fact that there is a certain inaccuracy in the documentation itself. In the FAQ section, you can find the mention "How do I set the charset and collation for MySQL tables?" and in response “You can't set these values ​​inside the annotations, yml or xml mapping files. To make a database work with the default charset and collation you should configure MySQL ".
    • check: Adds a check constraint type to the column (may not be supported by all vendors).

What next?

Such a dose for one day should be sufficient.

Doctrine is a very complex tool that cannot be presented in a single article.

Get ready for more because there is a whole series of topics and see you later!

Similar entries
lsb bulb

Have an idea? Let's talk

Have an idea? Let's talk about your project.