I am using symfony and doctrine as my ORM.
For available types I have:
array
simple_array
json_array
I am wondering what the difference is between each of them: when do I use one or the other?
Can I have a demonstration for each of them to illustrate the differences?
I already use simple_array in some applications but I find I don't understand formType... (Or maybe I'm not using it well!? )
To illustrate my question, here is an example:
I have an Task that I have to run on specific days
So I created TaskEntity with days attribute
Days would be:
$days = array(
1=>true,
2=>true,
3=>true,
4=>true,
5=>true,
6=>false,
7=>false
);
But I have no idea which of the above types to choose ...
For your problem simple_array is the right way, the right way may also create seven boolean fields.
However here's a little vademecum:
The best way to see how a type works in doctrine is to read the code of the type, this is because there are several details that are taken for granted or are not really explained in the documentation.
So you can go into
/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php
find your type and check if its methods work as you want.
Here some details:
simple_array
in /vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php
return implode(',', $value);
it's just a implode()/explode() of items, stores only the values and it's useful because you can easily query the database.
array
in /vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php
return serialize($value);
calls PHP to serialize()/unserialize(), it's faster than json_array. Looking at the code I think it also works with objects. Obviously if you see the field as plain text it's uncomprensible.
json_array
in /vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php
return json_encode($value);
it calls json_encode()/json_decode(), if you look in the field you can see a unformatted JSON array but it's more readable than PHP's serialized object and is really more portable (JSON exists everywhere).
June 2018 update
now there is a complete and more updated documentation here
json_array is deprecated in favor of json type, it will leverage on new database features for json fields
Another consideration: The most efficient way to represent a small set of true/false values like the ones presented here would be a bitfield.
That way you're only storing one integer instead of a full string. And you avoid the encode/decode overhead.
See https://stackoverflow.com/a/5412214/827254 for a good example.
The best solution for your problem, as stated, would be to use an array mapping of type array or json_array but not simple_array. The reason is that the serialization method of simple_array is just a call to implode(',', $array) and that would retain only the values and not the keys of the array, thus invalid for your situation where you have an associative array.
However, you could also model your $days attribute as a 0-based array (i.e. monday would be zero, tuesday would be 1, etc.). In that case, it would work because the deserializing with explode(',', $serialized); generates a 0-based array with the serialised values.
According to the Documentation:
Doctrine ORM > Basic Mapping > Doctrine Mapping Types
You have 3 choices regaring array data:
array Type that maps a SQL CLOB to a PHP array using serialize() and unserialize().
simple_array Type that maps a SQL CLOB to a PHP array using implode() and explode(), with a comma as delimiter.
IMPORTANT: Only use this type if you are sure that your values cannot contain a ,.
json_array Type that maps a SQL CLOB to a PHP array using json_encode() and json_decode().
So, if you are sure about not having , (comma) in your array values, use simple_array. If you have a simple array structure (linear), use array and if you have more complex key-value arrays, use json_array.
From www.doctrine-project.org reference
Array types
Types that map array data in different variations such as simple arrays, real arrays or JSON format arrays.
array
Maps and converts array data based on PHP serialization. If you need to store an exact representation of your array data, you should consider using this type as it uses serialization to represent an exact copy of your array as string in the database. Values retrieved from the database are always converted to PHP's array type using deserialization or null if no data is present.
simple_array
Maps and converts array data based on PHP comma delimited imploding and exploding. If you know that the data to be stored always is a scalar value based one-dimensional array, you should consider using this type as it uses simple PHP imploding and exploding techniques to serialize and deserialize your data. Values retrieved from the database are always converted to PHP's array type using comma delimited explode() or null if no data is present.
json
Maps and converts array data based on PHP's JSON encoding functions. If you know that the data to be stored always is in a valid UTF-8 encoded JSON format string, you should consider using this type. Values retrieved from the database are always converted to PHP's native types using PHP's json_decode() function. JSON objects are always converted to PHP associative arrays.
an other page with good description
Related
Let's say I have a MySQL JSON data type called custom_properties for a media table:
An example of the json data stored in the custom_properties column could be:
{
"company_id": 1,
"uploaded_by": "Name",
"document_type": "Policy",
"policy_signed_date": "04/04/2018"
}
In my PHP Laravel app I would do something like this:
$media = Media::where('custom_properties->company_id', Auth::user()->company_id)->orderBy('created_at', 'DESC')->get();
This would fetch all media items belonging to company 1.
My question is that lets say we have 1 million media records, would this be a bad way to fetch records in terms of performance? Can anyone shed some light on how MySQL indexes JSON data types?
Is the performance significantly better if we joined separate tables and index the columns instead? I'd like to know what the actual performance difference would be.
From the MySQL official docs:
JSON documents stored in JSON columns are converted to an internal
format that permits quick read access to document elements. When the
server later must read a JSON value stored in this binary format, the
value need not be parsed from a text representation. The binary format
is structured to enable the server to look up subobjects or nested
values directly by key or array index without reading all values
before or after them in the document.
When they say "quick read access" they mean "better than if you stored JSON in a TEXT column."
It's still bad performance compared to an indexed lookup.
Using JSON_EXTRACT() or the -> operator is the same as searching on any other expression in MySQL, in that it causes the query to do a table-scan.
If you want better performance, you must create an index on the field you search for. That requires defining a generated column before you can make an index.
See https://mysqlserverteam.com/indexing-json-documents-via-virtual-columns/
One of the former developers in an old system I'm working on used PHP serialize() to flatten one array of data into one column of a MySQL table.
I need to write a complex query that involves this column. Is there a way to unserialize the data (even if it's into a comma separated string) within the SQL query or do I need to pass the results back to PHP?
Serialized PHP data is just a string, so for "simple" searches, you can use basic MySQL string operations.
e.g. The serialized array has a name parameter and you need to match against that, so
... WHERE LOCATE(CONCAT('s:', LENGTH('foo'), ':foo') ,serializeddatafield) > 0
which would produce
WHERE LOCATE('s:3:foo', serializeddata) > 0
But then, this would find ANY strings whose value is foo anywhere in the data, and not necessarily in the particular array field you want.
e.g. you're probably better off yanking out the whole field, deserializing in PHP, and then doing the search there. You could use the above technique to minimize the total number of strings you'd have to yank/parse, however.
When I do a find or fetch, the returned array of values contains string representations of all my columns regardless of the column's data type. This means instead of integer columns returning like:
array( 'field1' => 1 )
they end up as
array( 'field1' => '1' )
Is this expected behavior for CakePHP? Did I misconfigure something?
I thought about writing some code in the afterfind method of my models to call intval on the appropriate columns, but is that really the best solution? Does CakePHP have a better way of handling non-string columns?
You can figure out what kind of field a field in the table your model is from the model property Model::_schema. Based on that you could type cast the values for your fields in the Model::afterFind() callback.
You can also manually check the fields you want to be integers but using _schema would allow you to automate it. You could do this as a behavior for example and attach it to every model that needs this functionality.
So simply iterate over the results and check for the fields you want to be integers and type cast them.
I noticed this too, and apparently it's the result of PDO casting everything to string. I submitted a ticket and hopefully there might be an automatic feature to coercion feature in the future.
Although the clients could convert the data, it's not very 'clean' especially when you might be providing the api to 3rd party developers.
One of my fields in my data base is an array which has been converted to a string using the implode() function.
How do I retrieve the contents of this field (LESSONS) from the database and store it to a string when a user entered value is equal to the the value of the field NAME?
Thanks in advance for any help you may be able to provide.
Here you go:
$r = mysql_query('SELECT LESSONS FROM TABLE WHERE NAME=\'user_string\'');
$rows = mysql_fetch_assoc($r);
echo $rows['LESSONS'];
I don't know if I understood your question but... Take a look about Stored Procedures
If you used the implode function to convert your array into a string, then this data has lost any information about the array keys.
As far as you want to convert the data back, use the explode function:
$array = explode(',', $columnData);
But You can therefore not search for array keys within the database.
Next to that, the MySQL database (I assume you're using MySQL) can not search for array keys anyway.
You need to store the data in some other way into the mysql to search for it in an SQL later on.
For example, you can create a table that stores key/value combinations with a grouping index.
However MySQL has some string functions that can help you searching within the (now) string data in the MySQL database.
When searching for a value, before the comparison add a comma at the beginning and one at the end of the string. There is a MySQL string function that can concatenate strings. Then search within that expression for your value with a comma added in front and back as well.
Then you can lookop a single array element within the mysql database. MySQL String Functions.
This is a quick solution only, this won't work on large databases performant. However it might solve your problem w/o changing your database structure.
I'm thinking of storing data from a form as a string, each answer separated by a pipe. The problem I have is that some answers may come in the form of multiple items. We store the radio button selection along with their corresponding answers e.g.
Question 1 - 1 Answer [A1]
Question 2 - Radio button selected [A2] + 3 form fields
Question 3 - 1 Answer [A3]
So I was thinking of storing the data like:
$str = A1|A2[x,x,x]|A3
The reason I chose to enclose multiple selections in brackets is in order to have it relate to the question.
I think my solution will work but when I come to read the values from the database I'll use Php's explode() to get the values into an array.
E.g. explode("|",$str);
Will give:
array(0=>A1, 1=>A2[x,x,x],2=>A3);
Before developing this, what would be the best way of getting the content of [x,x,x] and separating it from array[1]?
Any suggestion will be much appreciated.
Thanks
Alternatively, rather than having your answers as a string then trying to turn them to an array you could start with them in an array then if you need turn them into a string, so you could have:
$array = array('A1',array(x,x,x),'A3');
Then if you do need it as a string you can call:
$str = serialize($array);
That way, you get a similar method of access to your solution after you have exploded the string, only you don't have to deal with trying to split the A2[1,2,3] string further as you can just check if it's an array instead.
Your approach doesn't provide any benefit and it introduces an unnecessary complexity. Why don't you just use a relational database and store each piece of data a in a separate table row? If you don't have access to MySQL or a similar DBMS, you can always use SQLite.
I think you need to take a look at serialize and unserialize.
You could also store the information as an associative array encoded into json with json_encode() function. Then when you fetch data from the database, you would just do:
$arr = json_decode($value);
http://php.net/manual/en/function.json-encode.php