logged user INSERT-ing INTO joined tables in PHP - php

Here is the relational schema of my DB
Where 'articol' is 'paper', 'citare' is 'citation' in English:
A user is logging in with an ID and a Password and using the interface, he introduced his data in the DB. The code works very well for adding a 'paper' but now I want the user to introduce his 'citations' of the respective 'paper'. The problem is that the code allows introducing the citation in the table 'citation'('citare') but I do not know how to associate this 'citation' to 'paper' ('articol') and to the logged 'user' in the PHP code?
session_start();
$user = $_SESSION['username'];
$pass = mysql_real_escape_string(encrypt_decrypt('encrypt', $_SESSION['password']));
//there is code here...
$AddQuery="INSERT INTO `citare` (articolCitat, titluCitare, autoriCitare, publicatieCitare, isiCitare, volCitare, noCitare, anCitare, linkCitare) VALUES ('$_POST[narticolcitat]','$_POST[ntitlucitare]','$_POST[nautoricitare]','$_POST[npublicatiecitare]','$_POST[nisicitare]','$_POST[nvolcitare]','$_POST[nnocitare]','$_POST[nancitare]','$_POST[nlinkcitare]' )";
mysql_query($AddQuery, $link);
$last_id_inserted_from_citare = mysql_insert_id($link);
$AddQuery="INSERT INTO artcitare (idArticol, idCitare) VALUES ('{$_POST['hidden_articol']}','".$last_id_inserted_from_citare."')";
mysql_query($AddQuery, $link);

In terms of what you want to know, basically you cannot insert into 2 different tables at the same time (hence INNER JOIN does not help in this case). You need to do one first and then the other.
You can wrap those in a transaction, which will make it more robust.
The order is important. You first need to insert you main entities and then fill in the association tables for your many to many. That means that in order to associate "citare", "articol" and "user" they'll need to be inserted first. After that, you'll need to create the appropriate INSERTs in the "artcitare" and "userart" tables to associate them.
Having said that, I think that you might be confusing the process of persisting data into the database from its retrieval process. From your code sample, it seems that the entity "articol" is already persisted in the database, and you're only trying to associate "citares" to it. If the "articol" entity is already persisted, I'd assume that the corresponding "userart" association also exists. Hence, when you're inserting "citares" and associating them to "articols" via the "artcitare" association table you're already linking them to the user.

Related

Insert Array into MYSQL field

For a forum, i want to enable the users to send messages to each other to.
In order to do this, I made a table called Contacts, within this table I have 5 collumns: The user_id, a collumn for storing Friends, one for storing Family, one for storing Business and one for other contacts. These last four should all contain an array, which holds the user_id's of that type of contact. The reason I chose for this design is because I don't want to type an awful lot or limit the users on the amount of friends, like friend1, friend2 etc.
My question is: Is this correct how I do it? If not, what should be improved?And what type of MYSQL field should Friends, Family, Business and Other be?
What you should do instead of that is have a map table between your Contacts table and any related tables (User, Friends, Family, Business). The purpose would purely be to create a link between your Contact and your User(s) etc, without having to do what you're talking about and use arrays compacted into a varchar etc field.
Structured data approach gives you a much more flexible application.
E.g. UserContacts table purely contains its own primary key (id), a foreign key for Users and a foreign key for Contacts. You do this for each type, allowing you to easily insert, or modify maps between any number of users and contacts whenever you like without potentially damaging other data - and without complicated logic to break up something like this: 1,2,3,4,5 or 1|2|3|4|5:
id, user_id, contact_id
So then when you come to use this structure, you'll do something like this:
SELECT
Contacts.*
-- , Users.* -- if you want the user information
FROM UserContacts
LEFT JOIN Contacts ON (UserContacts.contact_id = Contacts.id)
LEFT JOIN Users ON (Users.id = UserContacts.user_id)
Use the serialize() and unserialize() functions.
See this question on how to store an array in MySQL:
Save PHP array to MySQL?
However, it's not recommended that you do this. I would make a separate table that stores all the 'connections' between two users. For example, if say John adds Ali, there would be a record dedicated to Ali and John. To find the friends of a user, simply query the records that have Ali or John in them. But that's my personal way of doing things.
I recommend that you query the users friends using PHP/MySQL all the time you need them. This could save considerable amount of space and would not take up so much speed.
serialize the array before storing and unserialize after retrieving.
$friends_for_db = serialize($friends_array);
// store $friends_for_db into db
And for retrieving:
// read $friends_for_db from db
$friends_array = unserialize($friends_for_db);
However, it should be wiser to follow other answers about setting up an appropriate many-to-many design.
Nevertheless, I needed this kind of design for a minor situation which a complete solution would not be necessary (e.g. easy storing/retrieving some multi-select list value which I'll never query nor use, other than displaying to user)

Saving Checkbox Changes to DB

I am looking for the best approach to save changes to a list of checkboxes to the database. Here is my setup:
I have a list of checkboxes that are either checked or unchecked, based on some database entries, as such:
Animals I like:
0 Cats
X Dogs
0 Birds
X Elefants
Now the user can completely change his/her mind and select Birds and deselect Dogs and I want to save this change in the database as efficiently as possible.
The way the db is structured, I have a user table (with a user_id) and an animal table (with an animal_id). The "likes" are tracked in a pivot table (because it is a many-to-many relationship).
Here are a few approaches I have considered, but I am interested in any other/better/more efficient ones:
1) On save, delete all entries in the pivot table for this user and enter only the checked ones.
This has the advantage that I don't need to compare much of the before/after choices. The disadvantage is that I delete an entry that has not changed (i.e. elefants in the above example). If I attach a creation timestamp for example to the elefant like, it will change every time, even when I don't change that selection
2) On save, I query the db to get a list of all original likes. The I compare this list to new the new likes. Every time I encounter an original like, that is not in the new list, I remove it. If I encounter a new like that is not in the original list, I add it.
This has the advantage of only changing the changes to the db, but it seems like an awful lot of queries. If the list of animals is long and many changes are made, the looping could result in a lot of db transactions.
So, what would be the best practice to solve this issue. I mean, it must be a common problem and I don't want to reinvent the wheel here.
option 1 would be the best, but since you don't want your timestamps disturbed, you CAN do a somewhat more efficient system than "check each record individually".
1) fetch a list of the user's choices from the db, $original.
2) fetch the list of choices from the submitted form, $submitted.
3) use array_diff() to figure out what's changed and what you need to do to the database:
e.g.
$original = array(2,4,6,8);
$submitted = array(4,7,8);
// so 2 and 6 have been removed, and 7's been added.
$unchanged = array_intersect($original, $submitted); // 4, 8
$removed = array_diff($original, $submitted); // 2, 6
$added = array_diff($submitted, $original); // 7
$sql = "DELETE FROM pivot_table WHERE animal_id IN (2, 6);"; // remove 2&6
$sql = "INSERT INTO (animal_id, ...) VALUES (7, ...)"; // insert 7
When you originally present the choices to the user you've done a database query to display what s/he has already checked. Now you can compare the changes to the original. If something changed, update, otherwise, leave it alone.

Save additional information to MYSQL Database and use a simple query, or use complex query?

I have a drupal site, and am trying to use php to grab some data from my database. What I need to do is to display, in a user's profile, how many times they were the first person to review a venue (exactly like Yelp's "First" tally). I'm looking at two options, and trying to decide which is the better way to approach it.
First Option: The first time a venue is reviewed, save the value of the reviewer's user ID into a table in the database. This table will be dedicated to storing the UID of the first user to review each venue. Then, use a simple query to display a count in the user's profile of the number of times their UID appears in this table.
Second Option: Use a set of several more complex queries to display the count in the user's profile, without storing any extra data in the database. This will rely on several queries which will have to do something along the lines of:
Find the ID for each review the user has created
Check the ID of the venue contained in each review
First review for each venue based on the venue ID stored in the review
Get the User ID of the author for the first review
Check which, if any, of these Author UIDs match the current user's UID
I'm assuming that this would involve creating an array of the IDs in step one, and then somehow executing each step for each item in the array. There would also be 3 or 4 different tables involved in the query.
I'm relatively new to writing SQL queries, so I'm wondering if it would be better to perform the set of potentially longer queries, or to take the small database hit and use a much much smaller count query instead. Is there any way to compare the advantages of either, or is it like comparing apples and oranges?
The volume of extra data stored will be negligible; the simplification to the processing will be significant. The data won't change (the first person to review a venue won't change), so there is a negligible update burden. Go with the extra data and simpler query.

Is it considered bad form to encode object-oriented data directly into single rows in a relational database?

I'm relatively new to databases so I apologize if there's an obvious way to approach this or if there is some fundamental process I'm missing. I'm using PHP and MySQL in a web application involving patient medical records. One requirement is that users be able to view and edit the medical records from a web page.
As I envisage it, a single Patient object has basic attributes like id, name, and address, and then each Patient also has an array of Medication objects (med_name, dose, reason), Condition objects (cond_name, date, notes), and other such objects (allergies, family history, etc.). My first thought was to have a database schema with tables as follows:
patients (id, name, address, ...)
medications ( patient_id, med_name, dose, reason)
conditions ( patient_id, cond_name, date, notes)
...
However, this seems wrong to me. Adding new medications or conditions is easy enough, but deleting or editing existing medications or conditions seems ridiculously inefficient - I'd have to, say, search through the medications table for a row matching patient_id with the old med_name, dose, and reason fields, and then delete/edit it with the new data. I could add some primary key to the medications and conditions tables to make it more efficient to find the row to edit, but that would seem like an arbitrary piece of data.
So what if I just had a single table with the following schema?
patients (id, name, address, meds, conds, ...)
Where meds and conds are simply representations (say, binary) of arrays of Medication and Condition objects? PHP can interpret this data and fetch and update it in the database as needed.
Any thoughts on best practices here would be welcome. I'm also considering switching to Ruby on Rails, so if that affects any decisions I should make I'm interested to hear that as well. Thanks a lot folks.
The 'badness' or 'goodness' of encoding your data like that depends on your needs. If you NEVER need to refer to individual smaller chunks of data in those 'meds' and 'conds' tables, then there's no problem.
However, then you're essentially reducing your database to a slightly-smarter-than-dumb storage system, and lose the benefits of the 'relational' part of SQL databases.
e.g. if you ever need to run a a query for "find all patients who are taking viagra and have heart conditions", then the DBMS won't be able directly run that query, as it has no idea how you've "hidden" the viagra/heart condition data inside those two fields, whereas with a properly normalized database you'd have:
SELECT ...
FROM patients
LEFT JOIN conditions ON patients.id = conditions.patient_id
LEFT JOIN meds ON patients.id = meds.patient_id
WHERE (meds.name = 'Viagra') AND (condition.name = 'Heart Disease')
and the DBMS hands everything automatically. If you're encoding everything into a single field, then you're stuck with substring operations (assuming the data's in some readable ascii format), or at worse, having to suck the entire database across to your client app, decode each field, check its contents, then throw away everything that doesn't contain viagra or heart disease - highly inefficient.
This breaks first normal form. You can never query on object attributes that way.
I'd recommend either an ORM solution, if you have objects, or an object database.
I'd have to, say, search through the medications table for a row
matching patient_id with the old med_name, dose, and reason fields,
and then delete/edit it with the new data.
Assuming the key was {patient_id, med_name, start_date}, you'd just do a single update. No searching.
update medications
set reason = 'Your newly edited reason, for example.'
where patient_id = ?
and med_name = ?
and start_date = ?
Your app will already know the patient id, med name, and start date, because the user will have to somehow "select" the row those are in before any change will make sense.
If you're going to change the dosage, you need two changes, an update and an insert, in order to make sense.
update medications
set stop_date = '2012-01-12'
where patient_id = ?
and med_name = ?
and start_date = ?
-- I'm using fake data in this one.
insert into medications (patient_id, med_name, start_date, stop_date, dosage)
values (1, 'that same med', '2012-01-12', '2012-01-22', '40mg bid')

Automatically joining tables without breaking default behaviour in Zend Framework

The situation is as follows: I've got 2 models: 'Action' and 'User'. These models refer to the tables 'actions' and 'users', respectively.
My action table contains a column user_id. At this moment, I need an overview of all actions, and the users to which they are assigned to. When i use $action->fetchAll(), I only have the user ID, so I want to be able to join the data from the user model, preferably without making a call to findDependentRowset().
I thought about creating custom fetchAll(), fetchRow() and find() methods in my model, but this would break default behaviour.
What is the best way to solve this issue? Any help would be greatly appreciated.
I designed and implemented the table-relationships feature in Zend Framework.
My first comment is that you wouldn't use findDependentRowset() anyway -- you'd use findParentRow() if the Action has a foreign key reference to User.
$actionTable = new Action();
$actionRowset = $actionTable->fetchAll();
foreach ($actionRowset as $actionRow) {
$userRow = $actionRow->findParentRow('User');
}
Edit: In the loop, you now have an $actionRow and a $userRow object. You can write changes back to the database through either object by changing object fields and calling save() on the object.
You can also use the Zend_Db_Table_Select class (which was implemented after I left the project) to retrieve a Rowset based on a join between Action and User.
$actionTable = new Action();
$actionQuery = $actionTable->select()
->setIntegrityCheck(false) // allows joins
->from($actionTable)
->join('user', 'user.id = action.user_id');
$joinedRowset = $actionTable->fetchAll($actionQuery);
foreach ($joinedRowset as $joinedRow) {
print_r($joinedRow->toArray());
}
Note that such a Rowset based on a join query is read-only. You cannot set field values in the Row objects and call save() to post changes back to the database.
Edit: There is no way to make an arbitrary joined result set writable. Consider a simple example based on the joined result set above:
action_id action_type user_id user_name
1 Buy 1 Bill
2 Sell 1 Bill
3 Buy 2 Aron
4 Sell 2 Aron
Next for the row with action_id=1, I change one of the fields that came from the User object:
$joinedRow->user_name = 'William';
$joinedRow->save();
Questions: when I view the next row with action_id=2, should I see 'Bill' or 'William'? If 'William', does this mean that saving row 1 has to automatically update 'Bill' to 'William' in all other rows in this result set? Or does it mean that save() automatically re-runs the SQL query to get a refreshed result set from the database? What if the query is time-consuming?
Also consider the object-oriented design. Each Row is a separate object. Is it appropriate that calling save() on one object has the side effect of changing values in a separate object (even if they are part of the same collection of objects)? That seems like a form of Content Coupling to me.
The example above is a relatively simple query, but much more complex queries are also permitted. Zend_Db cannot analyze queries with the intention to tell writable results from read-only results. That's also why MySQL views are not updateable.
You could always make a view in your database that does the join for you.
CREATE OR REPLACE VIEW VwAction AS
SELECT [columns]
FROM action
LEFT JOIN user
ON user.id = action.user_id
Then just use
$vwAction->fetchAll();
Just remember that views in MySQL are read-only (assuming this is MySQL)
isn't creating a view sql table a good solution to make joint ?
and after a simple table class to access it
I would think it's better if your logic is in sql than in php

Categories