I have a complex record stored in session. I want to update parts of this record in session then update the DB row with the session data. So far I have managed to overwrite the entire structure and not just the part of it I intend to.
Example:
base <-- whole record
base.field1 <-- single field
base.field2 <-- single field
base.field3 <-- single field
base.users <-- array of objects (users), stored as JSON column
base.details <-- single dimensional array of fields
base.cards <-- array of objects (cards), stored as JSON column
base.regions <-- array of objects (regions), stored as JSON column
This whole structure is stored as a row in a progress table.
I load and store this data in session like this:
session()->put('base', Progress::find($id));
I then update some of the fields in the cards array:
$cards = session()->get('base.cards');
$cards[$index]['points'] = 100;
I then try (unsuccessfully) to update the session variable, having tried both below:
session()->put('base.cards', $cards);
session()->push('base.cards', $cards);
session('base.cards', $cards);
Then lastly I want to store this record in the progress table by updating its instance, like this:
Progress::find($id)->update(session()->get('base'));
How do I manipulate then update in session just one of the JSON/array fields?
UPDATE: I added:
session()->put('base.cards', $cards);
session()->save();
But still, when I dd(session()->get('base')) I get the $cards array?!
I would suggest your problem is caused by you using Session in a way that was not intended. You seem to be storing an Eloquent model called Progress in the Session with the key 'base' and then expecting the get, put and push methods of Session to understand how to interact with your Model's parameters.
How about you make life simple and just store the ID in the Session...
$progress = Progress::find($id);
session()->put('progress_id', $progress->id );
session()->save();
And then when you want to pull it out to manipulate, save in database etc do this...
$progress_id = session()->get('progress_id');
$progress = Progress::find($progress_id);
$progress->cards = $cards->asJSON(); // Give your cards model a method that serialises itself
$progress->save();
Overall your code is now easier to read and everything's being used in a very 'normal' way. The session is just holding an ID, the model exists as a proper Eloquent model being accessed in the documented way.
Future you will be grateful when he comes to review this code :-)
Oh, and if you were storing the model in the session because you're worried about speed, pulling a single record from a properly indexed database is probably no slower than pulling the data from a session. We're talking thousands of a second but please do run tests to put your mind at ease.
Complex structures supported by session storage is limited to arrays.
To benefit from . syntax in key names, you need to convert the model to array, e.g.:
session()->put('base', Progress::find($id)->toArray());
So you can do later
session()->put('base.cards', $cards);
Related
Let's say I have a script which inserts rows into the database and it looks like this:
/* $start is some GET parameter. Any number between 0 and 9900 */
/* Select all objects with ids between $start and $start+99
and put their ids into $ids array */
$qb->select('object');
$qb->from('AppBundle:Object','object');
$qb->where("object.id >= $start");
$qb->andWhere("object.id < $start+100");
$objects = $qb->getQuery()->getResult();
$ids = array();
foreach($objects AS $object) {
$ids[] = $object->getId();
}
/* Create missing objects and insert them into database */
for($id=$start; $id<$start+100; ++$id) {
if(in_array($id, $ids)) continue;
/* Some calculations */
$createdObject = new Object($id, $some, $data);
$em->persist($createdObject);
}
$em->flush();
Now imagine there are no objects yet (the table is clear) and one user enters the site with start=0. The script takes like 2 seconds to complete. Before it finishes - another user enters the site with start=50.
I'm not sure what exactly would happen in such scenario, but I persume that:
First user enters - the $ids array is empty, the script is generating objects with id 0-99.
Second user enters - the $em->flush form the first entrance is not yet called, which means the $ids array is still empty (I guess?). The script is generating objects with id 50-149
There is a first $em->flush() call which comes from the first user entrance. It insert objects 0-99 into the database.
There is a second $em->flush() call which comes from the second user entrance. It tries to insert objects 50-149 into the database. It fails, because the object with id=50 already exists in the database. As a result it doesnt actually insert anything into the database.
Is that what would really happen? If so - how to prevent it and what is the best way to insert only those objects that are missing into the database?
#edit: This is just an exmaple code, but in the real script the id is actually 3 columns (x, y, z) and the object is a field on a map. The purpose of this script is that I want to have a huge map and it would take too much time to generate it all at once. So I want to generate only a little and then create the missing parts only when some user tries to access them. At some point the whole map will be created, but the process will be staggered.
You have 2 bad practices here:
You sould avoid INSERT or UPDATE operations when users enter your site (because they are slow/costly), especially if it's about adding many objects to the database like in this script. It should run in some kind of cron script, independently from your website users.
You shouldn't assign ID's to your objects beforehand. Leave it as null and Doctrine will handle it for you. Why would you need to set ID in advance?
To answer your question - if you call $em->persist() for an object with a pre-assigned ID, and in case another object exists in the database with the same ID - INSERT won't happen. Instead, the already existing object will be UPDATED with the data from your newer object (when you call em->flush() afterwards). So instead of 2 objects (as expected), you will have only 1 in the database. So that's why I really doubt if you need to pre-assign IDs in advance. You should tell me more about the purpose of this :)
I am trying to learn php databases and I have a question. How do I store an array in a database? I saw an answer on stackoverflow and there were they doing something with type double and in an other answer they were talking about creating a table for every user but I can't find a solution.
So summarized. I want to store an array in a database. I have acces to phpmyadmin so from there can I set the value type. and I would like to store that array in one column.
Can somebody help me solving the problem?
edit one: The things I want to store are music tags. So in code it would be something like this:
array('pop','rock','country');
I want to store It in one column to make it easy searchable
Rather than storing arrays, try this:
Table 'genres' :
id | name
1 | pop
2 | rock
Table 'songs' :
id | ...
1
Table 'songs_genres' :
song_id | song_genre
1 | 1
1 | 2
And use JOIN's to get the genres for each song (or whatever)
Usually you shouldn't store arrays in a single column in a db. It is preferable to have a table with tags and another one that links your entity with its tags. So, before try to store an array in a table just think if it is really the right thing to do in your specific case (usually not).
If you are sure you want to do that then you have serialize and unserialize functions.
UPDATE (after five years)
serialize and unserialize methods are inherently unsecure and should be avoided. It is preferable to store data in JSON format using json_encode and json_decode.
serialize() is one option here. You can serialize a PHP array into a string to store in the database, and when you return the string from the database you can unserialize() to convert it back from a string to an array.
[ Edit ]
After you've updated the question with an example of the data you plan to store, using a MANY:MANY relationship in the actual database structure is the correct way to go, as mentioned in #Alex M's answer
You can use json_encode to make a json string from the array, like so :
$jsonarray = json_encode($array);
then after retrieving the information you decode it.
$array = json_decode($jsonarray, true); // the true will turn it into an array,
otherwise it's an object.
but I'd advice against it. try making a database which has the proper columns and store your data trough there.
Every user can have one or more music tags. In later time they want to add or remove those tags. If you store all of their tags in one column you are pretty much left with string operation rather than database operation. create new table tbl_user_music_Tags and save each tag along with the user ID. this way you have full flexibility of adding, removing, updating and reading tags for a specific user.
You can store it as an string such as 'rock, pop, foo, ...'.
But if you want to manage tags, i think you should store tags in other table as #Alex M suggested.
Hello everyone and thank you for viewing this question.
Since someone asked what i am doing this for, here is the answer:
An artist asked me to make him a web app to store all his new concerts etc.. Now, when it comes to add the Instruments, artists etc, i could have 10 instruments, or maybe 100.. Everything is set into a form.. Some data is fixed like location, time etc, but this other fields are added dynamically using DOM..
I am building a system in which the user set up a form to be stored on a database like:
Name,Surname,field_1
//Lets say that this is the "fixed" part of the form
//But the user should be able to add 'n' other fields with no limit
//Therefore my problem is that i would end up with a row made of, lets say,
//4 colums
//And another one of, maybe, 100 columns
//
//Then i will need to access these rows, and row one should have 4 cols, row two 100..
//This can't be done in a "traditional" way since each row should have the
//same amount of cols
//
//I thought to create a new table for each submission
//but this doesn't really make that much sense to me..
//
//Storing all the possible fields in a single one and then
//access them through an array ? That would require too much, even since my fields
//should have the possibility to be edited..
//Each field is a mixture of variables then, like
//field1:a=12,field2:b=18.. too complex
Any help would be very appreciated
I would go the one field approach. You could have three columns, Name, Surname, and field_values. In the field_values column, store a PHP serialized string of an array representing what would otherwise be your columns. For example, running:
array(
['col1'] => 'val',
['col2'] => 'val1',
['col3'] => 'val2',
['col4'] => 'val3'
)
through serialize() would give you:
a:4:{s:4:"col1";s:3:"val";s:4:"col2";s:4:"val1";s:4:"col3";s:4:"val2";s:4:"col4";s:4:"val3";}
and you can take this value and run it back through unserialize() to restore your array and use it however you need to. Loading/saving data within this array is no more difficult than changing values in the array before serializing it and then saving it to the field_values column.
With this method you can have as many or few 'columns' as you need with no need for a ton of columns or tables.
In this case I would personally create a new table for each user, with new row inserted for ever new custom field. You must have a master table containing table names of each user table to access the data within later.
in a CakePHP 2.3 project, in one of the controller actions I want to update several records of a table. The data to be updated is posted as an array, and I iterate through that array.
However, some new field values are related to current field values, therefore I cannot simply write the data in an array $data and do model-save($data). Instead I do
$record = model->read(null, $id); //$id is retrieved from the posted data array.
$record['some_field'] = $new_value;
unset($record['modified']);
//in addition I used model->modified = null;, but to no avail
model->save($record);
Problem is, that the field modified is not automagically updated. In the CakePHP documentation I found that the value for "Modified" must not be present in the data that is to be saved. But the unset() alone doesn't seem to be enough.
In cakePHP - modified field not updating user tadasZ mentioned that it doesn't work when you use model->read() in advance.
I couldn't find anything about it in the documentation. But if that is the case, is there any way at all to use the Automagic for the field modified? I can set the field value myswlf (in fact, right now that's what I do as a workaround), but if there is an automatic way, I would like to use it.
When you are using Model::read(), the result is still in the same CakePHP format of $array['Model']['field'] so you would have to do unset($record['Model']['modified']);
The answer is here:
http://book.cakephp.org/2.0/en/models/saving-your-data.html#using-created-and-modified
If you have created or modified data in your $this->data (e.g. from a Model::read or Model::set) before a Model::save() then the values will be taken from $this->data and not automagically updated. If you don’t want that you can use unset($this->data['Model']['modified']), etc.
I'm new to "Object Oriented" PHP, but have managed to figure most things out so far. I'm building a website where users can register an account and then add, let's say, hobbies onto their profile.
I've managed to figure out creating the class, object, storing a single user in the database, and retrieving a single user from the database - creating a new object from the associative array that is returned from the SQL "select" query.
Where I am now getting stuck is when I need it to return multiple rows (records) from the database. For example, a user may have 7 hobbies; the SQL query returns all 7 rows into an associative array (with fields e.g. hobby_id, hobby_name, hobby_created), but how then do I make each one of those rows/hobby records into its own object?
I have tried searching all sorts of terms but I don't know if I'm just missing the buzz word that I need to search for. If anyone could please let me know the best way to go about this I would be eternally greatful.
Many thanks,
Steve
You can either loop through the result and create hobbies from the data or if you're using PDO you can use:
$stmt->fetchAll( PDO::FETCH_CLASS, 'Hobby' );
This will create a Hobby class for each row and populate properties with the columns from the query.
From http://www.php.net/manual/en/pdostatement.fetch.php
PDO::FETCH_CLASS: returns a new
instance of the requested class,
mapping the columns of the result set
to named properties in the class. If
fetch_style includes
PDO::FETCH_CLASSTYPE (e.g.
PDO::FETCH_CLASS |
PDO::FETCH_CLASSTYPE) then the name of
the class is determined from a value
of the first column.
Note: if you're using fetch() and not fetchAll() you have to use setFetchMode() before calling fetch()
$stmt->setFetchMode( PDO::FETCH_CLASS, 'Hobby' );
When you have an array of hobbies, create the hobby objects in a foreach.
$hobby = Array();
foreach ($query->results as $row)
$hobby = new Hobby($row['person_id'], $row['hobby']...
or perhaps a hash of objects.
First you'd have to actually create an object to handle those hobbies. You can't just sprinkle Acme OOP sauce on some data and have it magically turn into an object. An object is both data and functions (methods, actually) to work with that data. So, first figure out what you want your code to do with those hobbie data bits and work from there.
Is there any reason the hobbies needs to be its own object? It seems like it functions fine as a hash/array. If you really want to create an object, though, then you can create a hobby class that stores the hobby name/created date and has getters for them. Is there any other data to associate with the hobby, or calculations that need to be done? If so, you can create methods of the Hobby class to do this for you.
You can easily typecast an associative array to object with properties, like:
// $obj is instance of stdClass
$obj = (object)array('hobby_name'=>'name');
Of course the question wasn't that clear for what you need objects, or if they need to be of any explicit class (#Galean's answer).