I am working on a Many to Many table and I fixed an issue where I couldn't pull the records. Now I'm trying to delete records when that is needed.
I have a site where children can be associated to an event. In the event that a child was accidentally added I want to be able to remove the child. They are added via Checkbox as follows:
<input type="checkbox" name="eventChildren[]" id="childId_12" />
<input type="checkbox" name="eventChildren[]" id="childId_13" />
<input type="checkbox" name="eventChildren[]" id="childId_14" />
I have multiple checkboxes that are pre-checked if the child has already been added.
I have three tables (not sure if this is relevant):
DB:
- children
- events
- eventChildren
When I save the record I'm doing a check to make sure I don't add duplicates to my eventChildren table. I know if records are already in the database but not submitted from the form then the child is being removed from the list...and I can't figure out how to catch this subset.
Example code:
// $eventChildren comes from the form above name="eventChildren[]"
// $eventId is passed into the function
$currentChildren = $this->eventChildren->GetEventChildren(array('eventId' => $eventId));
// Loop through all submitted children
foreach ($eventChildren as $childId)
{
// Loop through all existing children
foreach ($currentChildren as $currChild)
{
// If the child ID's do not match then it's a new record
if ($currChild->childId != $childId)
{
$this->eventChildren->AddEventChildren(array(
'eventId' => $eventId,
'childId' => $childId
));
}
}
}
When the form is submitted I get a return that grabs all records from the eventChildren table where the eventId matches.
eventChildren (return)
- [0]
eventChildrenId => 1
childId => 12
eventId => 4
- [1]
eventChildrenId => 2
childId => 13
eventId => 4
- [2]
eventChildrenId => 3
childId => 14
eventId => 4
submittedChildren
- [0] => 12
- [1] => 13
How can I say, based on the submittedChildren array remove the record where eventChildrenId = 3?
I hope this makes sense. :)
There's many ways to approach this(better ways), but I'm just going to go with a straight forward answer I can give based on the info you presented.
$orphaned = array();
foreach ($currentChildren as $currChild) {
if (!in_array($currChild->childId, $submittedChildren)) {
$orphaned[] = $currChild->childId;
}
}
Just to be clear, you're deleting the relationship between the two items (i.e. deleting from the many-to-many table)?
Usually the event_id and the child_id fields would be the identifying (PRIMARY) key in the relationship. Therefore, if the user submits a child_id of 13, and you can contextually derive that the event_id is 4, then deleting from the eventChildren table where child_id = 13 and event_id = 4 should suffice. You shouldn't need to first gather information about the eventChildren object (you already have the identifying info).
Related
I have a table only able to submit maximum two times on each qid.
first submit = 'submitted' on status column.
second submit = 'resubmitted' on status column.
id username qid amount status
1 john 2 150 submitted
2 john 2 120 resubmitted
3 david 2 100 submitted
4 david 2 80 resubmitted
I want to add the first amount on a collection like below so I can simply display both amounts on view.
"id" => 1
"username" => john
"amount" => "120.00"
**"first_amount" => "150.00"**
"status" => "resubmitted"
Blade:
#foreach($xxx as $x)
{{$x->amount}}
{{$x->first_amount}}
#endforeach
Is this possible?
You can load data once:
$collection = Model::get();
And then use the collection without executing any additional queries:
#foreach($collection->where('status', 'resubmitted') as $x)
{{ $x->amount }}
{{ $collection->where('status', 'submitted')->firstWhere('qid', $x->qid)->amount }}
#endforeach
Or you can rebuild the collection to add first_amount to it:
$collection = $a->where('status', 'resubmitted')->map(function($i) use($collection) {
$i['first_amount'] = $collection->where('status', 'submitted')->firstWhere('qid', $i['qid'])->amount;
return $i;
});
Add the "first_amount" column to the table with a default value of 0. Then it will be populated in the collection when you retrieve it. You can set the value of the "first_amount" when you insert the "resubmitted" rows.
It will also save you having to modify the collection to add a value into it.
You can do this in your query or transform the result (via collection).
One possible solution (via Collection) is:
$collection = (new Collection($data))->groupBy('username')->map(function($userItems, $username) {
$firstAmount = $userItems->where('status', 'submitted')->first();
return $userItems->map(function($data) use ($firstAmount) {
return array_replace_recursive($data, ['first_amount' => $firstAmount['amount']]);
});
})->flatten(1);
This will add the first amount to all results.
Another way is like Alexey posted. Basically you fetch the the "resubmitted" values and add the first_amount into it. But, this will be added only into "resubmitted" values (items with 'submitted' won't have the first_amount key).
I'm having the hardest time figuring this out and it's probably because I'm not using the correct terms. If someone could point me in the right direction, that would be amazing. I'm going to use a hypothetical situation to make things easier:
I have a database with two tables:
tableA contains records for a house sale (house ID,address, price, current owner ID, etc)
tableB contains records for realtors who have shown a house (house ID, realtor ID, time and date, notes, etc).
I would like to have a query that can search a current owner ID and pull down all of their houses with information on everyone who showed the house. What I would like to retrieve is a JSON array that has the info from each tableB record appended/attached/added to a single record from tableA.
For example, if I search the the houses that are owned by ownerX (who owns two houses), I would like it to return two main items with sub items for each related entry in tableB. In the example below, ownerX has two houses. The first house on 1234 Fake St had 2 different realtors make a total of 3 visits. The second house on 555 Nowhere St had 1 realtor visit twice.
Here's how I'd like to retrieve the info:
tableA - Result 1 (House at address 1234 Fake St)
tableB - Result 1 (Realtor ID 1234, etc)
tableB - Result 2 (Realtor ID 1234, etc)
tableB - Result 3 (Realtor ID 2222, etc)
tableA - Result 2 (House at address 555 Nowhere St)
tableB - Result 1 (Realtor ID 1111, etc)
tableB - Result 2 (Realtor ID 1111, etc)
Instead, what I'm getting is this:
tableA - Result 1 (House at address 1234 Fake St),tableB(Realtor ID 1234, etc)
tableA - Result 2 (House at address 1234 Fake St),tableB(Realtor ID 1234, etc)
tableA - Result 3 (House at address 1234 Fake St),tableB(Realtor ID 2222, etc)
tableA - Result 4 (House at address 555 Nowhere St),tableB(Realtor ID 2222, etc)
tableA - Result 5 (House at address 555 Nowhere St),tableB(Realtor ID 2222, etc)
I don't don't want to retrieve tableA information each time. I only need that once, then each sub-result from tableB. This is important because I'm returning the data to an app that creates a new list. I'm currently using mysqli_multi_query
$sql = "SELECT * FROM tableA WHERE ownerID = "ownerX";";
$sql. = "SELECT tableB.*, tableA.houseID FROM tableB,tableA WHERE tableB.houseID = tableA.houseID;";
Again, the actual content is just a hypothetical. I'm looking for more of a, "You're an idiot, you should be using _____" and not, "You misspelled realtor and that's probably causing the problem.".
Also, please note that I'm not asking for the results to be formatted with the dashes and parentheses as they are above. I'm just simply writing it that way so it's easier to understand. I'm looking for a way to have sub-objects in a JSON array.
Any help pointing me in the correct direction would be much appreciated! Thanks to whoever takes the time to take a stab at this!
Tony
Additional information:
Here's the code I'm using to run the query:
$sql = "SELECT * FROM clocks WHERE user_key='".$userkey."';";
$sql .= "SELECT * FROM milestones WHERE (SELECT clock_key FROM clocks WHERE user_key='".$userkey."') = milestones.clock_key";
if (mysqli_multi_query($con,$sql))
{
do
{
if ($result=mysqli_store_result($con)) {
while ($row=mysqli_fetch_row($result))
{
$myArray[] = $row;
}
echo json_encode($myArray);
mysqli_free_result($result);
}
}
while(mysqli_more_results($con) && mysqli_next_result($con));
}
UPDATE WITH ANSWER:
Thanks for #vmachan's post below, I ended up getting all of my data at once, then ran through some loops to adjust the array. I'm going to use the house/relator example from above.
I used his code to get my results ($house_id is a variable input id):
$sql = "SELECT * FROM tableA INNER JOIN tableB ON tableA.houseID = tableB.houseID WHERE tableA.houseID='".$house_id."';";
I was given an array with 5 items because tableB had 5 entries. Since there are only 2 house entries in tableA, it looked like this:
["houseID"=>"1","price"=>"50000", "owner" => "Mike G", "state"=>"CA", "realtor" => "Jane D", "visitDay"=>"Tuesday", "notes" => "They liked the house"],
["houseID"=>"1","price"=>"50000", "owner" => "Mike G", "state"=>"CA", "realtor" => "Jane D", "visitDay"=>"Wednesday", "notes" => "They loved the house"],
["houseID"=>"1","price"=>"50000", "owner" => "Mike G", "state"=>"CA", "realtor" => "Stephanie W", "visitDay"=>"Friday", "notes" => "They didn't like the house"],
["houseID"=>"2","price"=>"65000", "owner" => "Michelle K", "state"=>"AL", "realtor" => "Mark S", "visitDay"=>"Tuesday", "notes" => "They made an offer"],
["houseID"=>"2","price"=>"65000", "owner" => "Michelle K", "state"=>"AL", "realtor" => "Jim L", "visitDay"=>"Monday", "notes" => "They stole stuff"]
The first 3 elements are from tableA and don't change. So, I used a loop to basically check the houseID, if it's a new house, create a new house array item, otherwise, add the details from tableB to the current house element:
<?php
//$house is an array will hold all of our indiviaul houses and their infomation.
$houseArray = array();
//Start the foreach loop
foreach($items as $item){
//$item["houseID"] is the houseID from our database that we got from the above code.
$houseID =$item["houseID"];
//$currentID is a varible that is set after the first iteration.
//This checks to see if we're still working with the same house, or a new house.
if($currentID!=$houseID){
//Create an array to hold all of the relator visit information arrays.
//This is created within the loop as it will erased if a new houseID is found in the array.
$relatorVisitArray = array();
//This is a secondary loop that checks the same array. This time, we are only working with the new houseID that from the condition above.
foreach($items as $rv){
//This cheecks to see if there is a match between the current houseID that we're working with and the other houseIDs in the array. Since we're going through the same array that we're already iterating, it will find itself (which is good).
if($houseID==$rv["houseID"]){
//Once is gets a match, it will create a temporary array to hold the "Relator Visit" information. The array is created within the loop as it needs to be cleared through each iteration.
$tempRealitorVisit = array(
'name' => $rv["name"],
'day' => $rv["day"],
'houseID' => $rv["houseID"],
'notes' => $rv["notes"]
);
//At the end of each iteation, we add the temporary to the main $relatorVisitArray.
$relatorVisitArray[] = $tempRealitorVisit;
}
}
//At this point, the subloop has ended and we're created an array ($relatorVisitArray) which contains all of the $tempRealitorVisit arrays.
//Remember, were are still within the first loop and have determined that this is a new house.
//Now we'll create a new house array based on the current houseID in this iteration.
//This array is created within the loop because we want it to cear at the next iteation when it's determined that it's a new house.
$house = array(
'houseID' => $item["houseID"],
'owner' => $item["owner"],
'price' => $item["price"],
'location' => $item["location"],
'relatorVisits' =>
//Here, we simply add the $relatorVisitArray to a key called, "relatorVisits" (ie an array within an array).
$relatorVisitArray
);
//We then add the $house to the $houseArray.
$houseArray[] = $house;
//Finally, we set $currentID to $item["houseID"]. At the next iteration, it will check this id against the next house ID. If they are the same, this entire code will skip until a new houseID appears from your database.
$currentID= $item["houseID"];
}
}
//This prints all of the information so it's easy to read.
echo '<pre>';
print_r($houseArray);
echo '</pre>';
}
?>
In the end, I'm left with one array that contains two sub arrays. The first sub array (House 1) contains 3 sub arrays (3 visits to that house). The second sub array (House 2) contains 2 sub arrays (2 visits to that house).
I hope this helps anyone that had the same issue as me. If anyone knows of a cleaner way to do this, please post it here! Thanks for the guidance!
Tony
I think you can combine the SQL statements as shown below to JOIN the clocks and milestones tables on the clock_key for a user-provided value i.e. $userkey. Then in your code you could loop thru the results and then check for consecutive house_ids.
$sql = "SELECT * FROM clocks INNER JOIN milestones ON clocks.clock_key = milestones.clock_key WHERE clocks.user_key='".$userkey."';";
You can then use the code similar to the one in ths SO posting. You would need to change it so that inside the loop you check if the previous 'house_id' is the same as the current one and if not, you would start a new parent array other wise keep adding to the existing array. At the end of the loop you could then call the encode to get your JSON format.
Hope this helps.
I have not got time to actually write (and test!) proper code but I would suggest that you collect your data into two php associative arrays: $houses with the owner as the key and $visits with the houseID as a key. Assuming that an owner can have more than one property on the market and knowing that a realtor can pay more than one visit to each property the entries in these two arrays will then themselves be "array of array"s.
sample:
$houses={'ownerx':{'houseID_1':['address_1','price_1'],
'houseID_2':['address_2','price_2']}},
'ownery':{'houseID_3':['address_3','price_3'],
'houseID_4':['address_4','price_4']}}
};
$visits={'houseID_1':['realtorID_1','realtorID_2', ...],
'houseID_2':['realtorID_3']
};
// I used JSON notation for simplicity ...
Doing this would require you to set up a proper structure once but it will save you from querying the same data again and again. The retrieval of the data from the associative arrays should also work very efficiently.
I have a table of data as such:
id | item | parent_id
1 item 1 0
2 item 2 0
3 item 3 2
4 item 4 3
5 item 5 1
...
The id is autoincrementing, and the parent_id reflects the id on the left. You may have come accross a database table design like this before.
The parent_id is not sequential as you can see.
I need to get this table data into an array in the format where all parents become a potential heading with their children underneath.
So I am looking at a structure like this:
Item 1
Item 5
Item 2
Item 3
Item 4
etc
In PHP I need an array structure that can display the above. But I am having a serious brain fart!
Can anyone help me with the array structure?
you may write somethin like this:
$a = Array(item1 => Array(item5), item2 => Array(item3 => Array(item4)))
or
$a = Array(item1 => parentid, item2 => parentid2 ....)
in the first example one item is the key for all ist children stored in the Array, in the other example all items are stored using an item key and an int value. this int value is the key for the parent item. which method you use depends on what you want to do with this Array. maybe both of my ideas are not good enough for your Needs. if thats the case, tell me what you need.
First of all, i will suggest you to read this, it's very useful for hierarchical structured data and there are available queries which will help you to get parents, children, etc ... and so and so.
Now to answer your question, try this :
$result = array();
while($data = mysql_fetch_assoc($query)) {
$id = $data['id'];
$parent = $data['parent_id'];
$keys = array_keys($result);
if(in_array($parent, $keys)) {
$result[$parent] = array_merge($result[$parent], array($id => $data['item']));
} else {
$result = array_merge($result, array($id => $data['item']));
}
}
I have two tables I'm working with: categories and businesses. The categories table looks like this:
id name parent
1 Automotive NULL
2 Tires 1
3 Oil Change 1
4 Home Renovations NULL
5 Painting 4
6 Landscaping 4
7 Bathroom 4
Basically, any category that has parent as NULL is a parent. Anything that is a child of it references it's ID in the parent column. Simple.
I have businesses stored in a table, and each business has categories. The categories are stored as json_encode so they look like this:
["1","4","5","13"]
The user can add a subcategory without adding a parent, so some businesses only have subcategories.
If I want to get the total number of business for a parent category INCLUDING subcategories, here's what I'm doing:
$parent_categories = $this->db->order_by('name', 'asc')->get_where('categories', array('parent' => NULL));
$businesses = $this->db->select('category')->get('businesses');
foreach ($parent_categories->result() as $parent):
$child_categories = $this->db->order_by('name', 'asc')->get_where('categories', array('parent' => $parent->id));
$parentChildCategories = array();
array_push($parentChildCategories, $parent->id);
foreach($child_categories->result() as $child):
array_push($parentChildCategories, $child->id);
endforeach;
// CONTINUED BELOW
At this point, if i print_r($parentChildCategories), I get the following (excluding a bunch of other category arrays, just focusing on one):
Array ( [0] => 81 [1] => 80 )
So this is the parent category id as well as the child category id. This parent category only has one child, but others might have multiple. This appears to work.
Now I want to go through each businesses category field, decode the json into a PHP array ($categories_array), then see if the above array ($parentChildCategories) is in it. If it is, I echo 'yep'.
foreach($businesses->result() as $business):
$categories_array = json_decode($business->category);
if (in_array($parentChildCategories, $categories_array)):
echo 'yep';
endif;
endforeach;
The problem is, I never get 'yep'. Nothing. So I `print_r($categories_array)' and it gives me the following:
Array ( [0] => 80 [1] => 81 )
The array values are the same as $parentChildCategories, but they are in different positions. So in_array doesn't see it as being in the array.
I'm banging my head against a wall trying to figure this out. Is there a better way of doing this? I'm obviously doing something wrong. Any help would be greatly appreciated.
Why do you store the categories related to businesses this way? If you'd normalise your database, you wouldn't have this problem in the first place.
I'd suggest creating a new table 'business_category_coupling', with 2 columns: business_id and category_id. That's basically all you'll ever need and eases maintenance dramatically.
The reason in_array does not work is that it checks whether the first array is an element in the second array - which, of course, it is not. Without going through the full logic, to do your comparison, you can use array_diff:
$ad = array_diff($parentChildCategories, $categories_array);
if(count($ad)) {
echo 'yep';
}
This code finds all elements from $parentChildCategories that are not present in $categories_array. If there are none, then you output yep.
I have the following mysql db row.
id | user_id | title_1|desc_1|link_1|title_2|desc_2|link_2|
and so on up to 10
from this one row I want to remove id and user id and have the resulting multidimensional array.
the main issue is iterarating over the associative array that is returned by my query and splitting it up into arrays of 3.
Array = (
[0] = array (
[tite_1] => 'sometitle'
[desc_1] => 'description'
[link_1] => 'a link'
)
[1] = array (
[tite_2] => 'sometitle'
[desc_2] => 'description'
[link_2] => 'a link'
)
and so on how can I achieve this I am stumped!!?
You probably want to structure your table into two tables like this:
parent(id, user_id, more_fields, whatever_you_need_here)
child(parent_id, title, desc, link)
Now it'll be very easy to get the data that you want to have.
SELECT title, desc, link FROM child WHERE parent_id = 12;
Of course, parent and child should be named appropriately, e.g. user and links.
The correct answer would be to redesign your database to use 3rd normal form. You should probably drop everything and read up on database normalization before you do anything further.
A proper design would be something like:
CREATE TABLE user_has_links (
id INT PRIMARY KEY,
user_id INT,
title TEXT,
description TEXT,
link TEXT
)
To store multiple links per user, you would simply create a new row in this table per link.
The real solution here is to fix your database to normalize these columns into other tables. However, if you are not in a position to fix your database, this code will do the job:
// $output will hold your full result set
$output = array();
while ($row = mysql_fetch_assoc($result)) {
// For each row returned, add a new array to $output
$output[] = array(
// The new array consists of 10 sub-arrays with the correct
// keys and values
array (
"title"=>$row['title1'],
"desc"=>$row['desc1'],
"link"=>$row['link1']
),
array (
"title"=>$row['title2'],
"desc"=>$row['desc2'],
"link"=>$row['link2']
),
...,
...,
array (
"title"=>$row['title10'],
"desc"=>$row['desc10'],
"link"=>$row['link10']
)
);
}