I'm looking at creating a 'Multi Page Navigation System' using PHP and MySQL along with jQuery.
Ideally, what I want is to be a a list of items, such as:
* Item 1
* Item 2
* Item 3
Within Item 1, there is 3 other subcategories, within Item 2 there are 2 subcategories and within them 2 subcategories there are 3 more subcategories
So what I'm really looking for is the following:
1) when i click on 'Item 2` is displays the 2 subcategories
2) when I click on one of these subcategories it displays the 3 others
Ideally, I'd like to do this in using AJAX as I'd like this to be in a jQuery UI Dialog.
I have 2 tables:
category
id | title
----------
1 | item 1
2 | item 2
3 | item 3
subcategory (simplified)
id | cat_id | parent_id | title
-------------------------------
1 | 1 | 0 | subcat1
2 | 1 | 0 | subcat2
3 | 1 | 1 | subcat1_subcat1
4 | 1 | 1 | subcat1_subcat2
5 | 1 | 1 | subcat1_subcat3
My main issue is how I'd go about doing this?
I don't really want to have a big array with all that data in, as it could potentially have more categories and subcategories.
Does any have an idea what would be the best solution to about this?
Thanks
What's wrong with having it all stored in an array? Unless you're planning on have thousands of elements in those menu items (which would be incredibly un-user-friendly), then it's just a walk in the park for PHP.
Also, you might want to be abit more specific about what your requirement is. Is it the jQuery, the PHP or both? Do you need the code or the concept?
EDIT: Solution
So based on the comments you listed, here's a proof of concept.
PHP:
You'll need to read from the Database and load them into an array. That's pretty easy to do with PDO. Just use the fetchAll() command and retrieve the entire result set in an associative array. The tricky part becomes converting your 'flat' DB into a multi-dimensional array. Here goes:
// Retrieve the details from the database in the $rows associative array
...
// Now, we need to expand the 'flat' DB structure into a multi-
// dimensional array.
$multid=findKids($rows);
// Send it back, JSON-encoded
die(json_encode(
'result' => 'success',
'response' => $multid
)); // Send the response back via AJAX
/**
* Here's the function that converts the flat DB into a multi-
* dimensional array. It tracks all the parents in a single
* array and collects the kids for those parents. If it comes
* across a new parent, if fetches all the kids recursively.
*/
function findKids( $rows, $parentid=NULL, &$parents=NULL ) {
// Create a temporary array for the kids
$shelter;
// Go through all the rows
foreach($rows as $index => $row) {
// Find the kids that belong to this parent
if($row['parentid']==$parentid) {
// This kid belongs to this parent
// Move it to the temporary shelter
$shelter[$parentid][]=$row;
}
else {
// This kid doesn't belong to this parent.
// Have we already talked to the parent before?
if(isset($parents[$row['parentid']])) {
// Yes, the parent has already been visited. Ignore
}
else {
// Parent hasn't been visited; add it
$shelter[$row['parentid']][]=findKids($rows,$row['parentid'],$parents);
}
} // close else{ }
} // close foreach{ }
// Return the shelter, with all the kids
return $shelter;
}
The returned array will include a response that looks like this:
$response=[
'result'=>'success',
'response'=>[
0=>[ // Contains the kids for $parentid=0
['id'=>1, 'cat_id'=>1, 'parent_id'=>0],
['id'=>2, 'cat_id'=>1, 'parent_id'=>0]
],
1=>[ // Contains the kids for $parentid=1
['id'=>3, 'cat_id'=>1, 'parent_id'=>1],
['id'=>4, 'cat_id'=>1, 'parent_id'=>1],
['id'=>5, 'cat_id'=>1, 'parent_id'=>1]
],
]
];
jQuery: You'll interpret the JSON response and iterate through the response to create the menu on the fly.
Here's a sample script that'll display the array as a nested unordered list.
// Call the printMyFamily method on the element to display
// that you'd like to see the menu appear in
$outputElem.html($.printMyFamily(NULL,response['response']));
$.printMyFamily = function(parentid,haystack){
// Our output variable
output="<ul>";
// Find the kids
$haystack.each(function(index,elem){
// Is this kid ours?
if(elem[parentid] == parentid){
// Yes. Add to output
output+="<li id='"+elem['id']+"'>"+elem['catid']+"</li>";
// Find this items kids (if any)
output+=$.printMyFamily(elem['id'],haystack);
}
else { /* not ours, ignore */ }
});
// Is the result an empty string? If so, just clear it
if(output=="<ul>"){ output=""; }
// Return the output
return output;
};
Related
I have a Document Model which represents a Document (name, description etc). I then have a DocumentData Model which represents the data of a Document. A Document has One to Many DocumentData,
So to create a Document I have a form. Lets say that my form has a text input for Document Owner. Within my DocumentData table, the label documentOwner is set as the key and the inputted data is set as the value. So with multiple form elements, my DocumentData table might look like this
document_data
id | documentId | key | value |
----------------------------------------------
1 | 1 | clientName | Google |
----------------------------------------------
2 | 1 | projectName | Analytics |
----------------------------------------------
3 | 1 | Contact | Mr Sharp |
----------------------------------------------
4 | 1 | startDate | 29/12/2016 |
----------------------------------------------
The Document name and description for the Document table is created using hidden fields within the view of the Document.
So I can create Documents without a problem. I am having a problem with my update function though. So far I have the following
public function update(Request $request, Project $project, $id)
{
$document = $project->document()->where('id', '=', $id)->first();
$docData = $document->documentData()->get();
$input = $request->all();
foreach($docData as $orgData) {
foreach($input as $key => $value) {
if($key !== "filePath" && $key !== "documentType" && $key !== "documentTypeDesc") {
if($key == $orgData->key) {
$orgData->value = $value;
$orgData->update();
}
}
}
}
return View::make($document->name.'Doc.edit', compact('project', 'document'));
}
So firstly I get the Document I am working on and store it in $document. I then get the DocumentData for this Document and store it in $docData. $docData is a collection containing my key-value pairings for a Document.
I then loop both the $docData and the inputted data and where the keys match, I reset its updated value.
However, at the moment, it updates everything to the last inputted data field. I am not sure where else I can perform the update operation, but I only need it to update the row it is referring too, not the whole thing.
How could I go about doing this?
Thanks
I've cleaned up the code a little, and made a few implementation changes. This code should work, so let us know if it doesn't, and if not, what failed.
public function update(Request $request, Project $project, $id)
{
// you can use eager loading and find on the relationship query
$document = $project->document()->with('documentData')->find($id);
// you can just access the relationship attribute, no need for the query
$docData = $document->documentData;
// gets all input, except the keys specified
$input = $request->except(['filePath', 'documentType', 'documentTypeDesc']);
foreach($docData as $orgData) {
// check if the input has a key that matches the docdata key
if (array_key_exists($orgData->key, $input)) {
// update the value
$orgData->value = $input[$orgData->key];
// use save, not update
$orgData->save();
}
}
return View::make($document->name.'Doc.edit', compact('project', 'document'));
}
I have a problem updating a multidimensional array in PHP. I'm trying to implement am e-commerce website for a project and I am having problems with the shopping cart implementation.
Basically, I use a session to track the items the user adds to the shopping cart. Here's my logic in plain Pseudo code that is executed once the user clicks the add button after specifying a quantity to add for a product:
RETRIEVE 'cartItems' 2D array from SESSION array
IF 'cartItems' array does not exist in the session create new empty array and add the cartItem sub array to it with the qty and productID
ELSE Loop through the array retrieved from the SESSION array, find the product ID that matches the given product ID (index 0) and update the qty for that subarray (index 1).
Here is my PHP script addToCart.php which in turn calls another function in another script file that is included in it:
<?php
require_once("cart_utility.php");
session_start();
// Script for adding a given product to the client's cart through the use of Ajax and sessions
// retrieve values from ajax request
$productID = $_GET["productID"];
$qty = $_GET["qty"];
$cartItems = null;
// use sessions to add the items to the user's cart
// retrieve the multi-dimensional cart array if it exists, otherwise create one and add it
if(isset($_SESSION["cartItems"])){
$cartItems = $_SESSION["cartItems"];
}
else{
$cartItems = array();
}
addQtyForProduct($productID, $qty, $cartItems);
$_SESSION["cartItems"] = $cartItems;
print "Session cartItems after function return: ";
var_dump($_SESSION["cartItems"]);
// return info string with new qty of cart items
print ("success-" . getTotalCartItems($cartItems));
?>
And here's the other script file that does the handling of inserting and updating the array:
<?php
// Utility functions for retrieving items from the 2D cart items array
/* The array structure is given as (example values):
* | productID | qty |
* 0 | 1 | 3 |
* 1 | 2 | 1 |
* 2 | 5 | 8 |
* 3 | 8 | 3 |
*/
// increments the qty for the given product. If it does not exist then it is added into the main session array
// $cartItems: the main 2D array with the structure given above, pass by reference to change the array
function addQtyForProduct($productID, $qty, &$cartItems)
{
foreach($cartItems as $cartItem)
{
var_dump($cartItem);
if($cartItem[0] == $productID){
//print "Quantity given to increment: $qty";
//var_dump($cartItem);
print "Qty in current session array: $cartItem[1]";
$cartItem[1] += $qty;
print "New qty in cartItem array: $cartItem[1]";
return;
}
}
// not found, therefore add it to the main items array
array_push($cartItems, array($productID, $qty));
}
// returns the total number of items in the cart
function getTotalCartItems($cartItems)
{
$total = 0;
foreach($cartItems as $cartItem)
$total += $cartItem[1];
return $total;
}
?>
I have placed some var_dump statements and can confirm that upon returning from the function 'addQtyForProduct', the array is not updated. But why? I pass the array by reference to directly alter it's contents.
It successfully adds on the first time when there is no existing array but fails to increment if the array exists.
Also, the values are successfully incremented in the 'addQtyForProduct' function but the array somehow is not updated when it returns from the function.
I would gladly appreciate some help on this. I've been trying to understand this for days now and It's driving me nuts.
As read on this page you should use references. Add a & in front of your $cartItem and your script should work. Right now PHP stores a 'copy' of every value in your array in $cartItem, rather than it's reference. So currently you are editing a copy of the original, rather than the original array.
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'm working with symfony using Propel trying to create functionality for back and next buttons:
$c = new Criteria();
$c->add(CartPeer::CATEGORY, $category);
$c->add(CartPeer::ITEM_ID, $item->getItemId(), Criteria::GREATER_THAN);
$c->addAscendingOrderByColumn(CartPeer::ITEM_NAME);
$this->next = CartPeer::doSelectOne($c);
Now, this works fine if the item's identifiers are in ascending order, but usually this is not the case.
How can I modify this code so it selects the item immediately after the current $item in the list of records returned instead of selecting one with the next ascending numerical ID?
Example:
Record: 0 | ItemID: 5
Record: 1 | ItemID: 2
Record: 2 | ItemID: 7 <-- $item
Record: 3 | ItemID: 4 <-- I want this to be $next
Record: 4 | ItemID: 9
I don't actually use Symfony, but if you're using an even remotely recent version of Propel with it then you have access to the paginate() method which may be a lot better in the end for you.
$pager = CartQuery::create()
->filterByCategory($category)
->orderBy(CartPeer::ITEM_NAME)
->paginate($pageToLoad, $resultsPerPage);
foreach($pager as $result) {
// do something with the record
}
if ($pager->haveToPaginate()) {
// build some paging items using PropelModelPager methods:
if (!$pager->isFirstPage()) {
echo "<a href='?page=".$pager->getPreviousPage()."'>Previous</a>";
}
if (!$pager->isLastPage()) {
echo "<a href='?page=".$pager->getNextPage()."'>Next</a>";
}
}
If you really want to do it your way, you may want to eliminate the restriction by ItemId entirely and simply add an offset along with your limit:
// This will be however many results have already been shown (page size?)
$offset = 10;
$c = new Criteria();
$c->add(CartPeer::CATEGORY, $category);
$c->addAscendingOrderByColumn(CartPeer::ITEM_NAME);
$c->setOffset($offset);
$this->next = CartPeer::doSelectOne($c);
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.