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.
Related
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.
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
I currently have about 4 different database tables which output to html tables. Each of these tables uses a count query to calculate data from a 5th table.
That's no problem, but what about when I want to sort and order the data, and paginate etc (like with zend). If it were a one page table, I could probably sort an array.
My thought was, to use a ticker. But that would require a new column in all 4 tables and seems like overkill or like there could be a better way.
Sadly, I can't find much info on it (likely because I don't know what to search for).
Advice?
..and please take it easy, I'm new and learning.
Assuming youre using Zend_Db_Table_Row and that you dont need to persist any modifications you might make to these rowsets then you can just append the virtual columns to the row object and have them be accessible via array notation. So if youre doing it all in one query now just use that same query, and the column should be there.
OTOH, if youre using a Data Mapper pattern then simply adjust your hydration to look for this "virtual column" and hydrate it if it exists in the result data. Then in your getter for this property have it see if the property is null or some other negative specification, and if it is, to execute a calculation query on that single object or return the already calculated result.
I have in my database a coloumn say agent which can have enum values ('G','Y','M') where
G= google
Y= yahoo
M= msn
how should i map the output coming from my function i.e google as G in my code so that database recognise it and saves it??
I think the easiest way is to get the int representation of the enum value. Enums are numbers in the background, so it's usually easy to cast an enum member to a number and vice versa.
You can write a converter class for the best flexibility. You can process the enum value and generate a string, or get a string and return an enum member. You can do this in a simple if-then-else or switch-case structure.
If you are using .NET (C#), be aware that you can pass any number as a parameter even you specify an enum, so an else or a default branch is a must in case of conversion.
If you are using PHP, here is an example:
$myEnum = array ("red" => 1,"blue" => 2,"yellow" => 3);
$d = $myEnum["red"];
You will get 1 for red, 3 for yellow. If you want to use a string:
$myEnum = array ("Google" => "G","Yahoo" => "Y","MSN" => "M");
$d = $myEnum["Google"];
You're already using ENUM data types, which are just an integer. Why not (in your DB schema, where you define ENUM), just define ENUM('Google','Yahoo','MSN', ...)? That's what I do -- then, when you're pulling/pushing data, you can just use the literal enum value (PHP will treat it like a string when you grab data from the db, and MySQL will treat the string passed from PHP as the enum type, assuming it's the same).
There's really no reason to use 'G' over 'Google', unless you're really that lazy of a typist. And if you're that lazy, why would you want to put another layer of indirection into your code, unnecessarily?
Edit: Sorry for the tone of the post -- it came off as far more harsh than intended. I just happen to feel pretty strongly against unnecessary indirection.
Since Cake does not support enums, you might want to use tinyint(2) fields and this approach:
http://www.dereuromark.de/2010/06/24/static-enums-or-semihardcoded-attributes/
It is faster, easier to maintain and also works with your (custom) bake templates.
What is the way to get the greatest value into a serialized data. For example i have this in my column 'rating':
a:3:{s:12:"total_rating";i:18;s:6:"rating";i:3;s:13:"total_ratings";i:6;}
How can I select the 3 greatest 'rating' with a query?
thanks a lot
You're probably looking at a pile of SUBSTRING_INDEX(field,':',#offset) calls if you want to do it in SQL. It would be very grisly. Storing a serialized version of an object in the db is a convenience for persistance, but it should not be considered a permanent storage method. If you insist on using the serialized string for queries, you've lost all the power of a relational db and you might as well store the strings in a text file.
The best option is to use the serialized string only for persistance purposes (like remembering what the user was doing last time they visited), and store the data you need for calculations in properly normalized fields and tables. Then you can easily query what you need to know.
The other option is to select all the 'rating' strings from rows whos fields meet certain other criteria (e.g. the date_added field is within the last week), reinstantiate all the objects in your application layer and compare them there.