Reputation: 37
I have this page in my system that displays all the categories entered. The table has three fields as follows:
The homepage displays all entries (blog) and is grouped by category. Now the idea is the administrator can change the 'priority' of a 'category' so that the page will be arranged based on what he sees fit.
Example:
==============================
| Category | Priority |
==============================
| Category1 | 1 |
| Category2 | 2 |
| Category3 | 3 |
| Category4 | 4 |
| Category5 | 5 |
===============================
The idea is when the user edits the priority of, let's say, category 5 to 1, the rest of the priorities of each category less than the priority edited would be adjusted by incrementing it by 1 so that there would be no duplicates.
So far I only have this.
if (isset($_POST['edit'])) {
if (is_numeric($_POST['cat_id']))
include('connect.php');
$id = $_POST['cat_id'];
$category = $_POST['category'];
$priority = $_POST['priority'];
mysql_query("UPDATE category
SET category='$category', priority='$priority'
WHERE cat_id='$id'") or die(mysql_error());
header("Location: addctg.php");
}
Any help on how to structure?
Upvotes: 0
Views: 67
Reputation: 753970
Let's deal with a more general case than moving Category5 from priority 5 to priority 1. There are two (mirror image) better examples:
In each of these cases, there are two unaffected rows (those with priority 1 and 5), one row that is specified as moving, and two rows affected as 'collateral damage'.
Most of the trick with an UPDATE statement is working out a SELECT statement that generates the correct data for the 'after' state from the 'before' state. The other trick is identifying the parameters that control the operation. Here, there are two parameters:
We might need separate algorithms for 'move up' (move a lower priority category to a higher priority), and for 'move down' (move a higher priority category to a lower priority).
Here are the before and after scenarios illustrated:
Before Category4 to Priority 2 Category2 to Priority 4
======================== ======================== ========================
| Category | Priority | | Category | Priority | | Category | Priority |
======================== ======================== ========================
| Category1 | 1 | | Category1 | 1 | | Category1 | 1 |
| Category2 | 2 | | Category4 | 2 | | Category3 | 2 |
| Category3 | 3 | | Category2 | 3 | | Category4 | 3 |
| Category4 | 4 | | Category3 | 4 | | Category2 | 4 |
| Category5 | 5 | | Category5 | 5 | | Category5 | 5 |
======================== ======================== ========================
Let's try the 'move up' algorithm first.
Using TDQD — Test-Driven Query Development — we can generate the list in stages:
Rows that are unaffected at high priority:
SELECT cat_id, category, priority
FROM categories
WHERE priority < 2;
Rows that are unaffected at low priority:
SELECT cat_id, category, priority
FROM categories
WHERE priority > (SELECT priority FROM categories WHERE category = 'Category4');
The row that is moved:
SELECT cat_id, category, 2 AS priority
FROM categories
WHERE category = 'Category4';
The rows that have to have their priority adjusted:
SELECT cat_id, category, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4');
Clearly, these 4 queries can be combined via a 4-way UNION to generate the complete new ordering. Equally clearly, since queries 1 and 2 select rows that don't change, we only need to process the rows that do change, which are generated by queries 3 and 4.
So, the changed rows are:
SELECT cat_id, category, 2 AS priority
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id, category, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4');
Now we just have to derive an UPDATE statement to change the priorities (without changing the category ID or the category name):
UPDATE categories
SET priority =
(SELECT x.priority
FROM (SELECT cat_id, 2 AS priority
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4')
) AS x
WHERE x.cat_id = categories.cat_id
)
WHERE cat_id IN
(SELECT cat_id
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4')
);
The WHERE clause in the main UPDATE statement can be simplified, yielding:
UPDATE categories
SET priority =
(SELECT x.priority
FROM (SELECT cat_id, 2 AS priority
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4')
) AS x
WHERE x.cat_id = categories.cat_id
)
WHERE cat_id IN
(SELECT cat_id
FROM categories
WHERE priority >= 2
AND priority <= (SELECT priority FROM categories WHERE category = 'Category4')
);
This produced the correct answer when tested against IBM Informix Dynamic Server 11.70.FC2 running on Mac OS X 10.7.4.
The parallel analysis for 'Move Down' yields the UPDATE statement:
UPDATE categories
SET priority =
(SELECT x.priority
FROM (SELECT cat_id, 4 AS priority
FROM categories
WHERE category = 'Category2'
UNION
SELECT cat_id, priority - 1 AS priority
FROM categories
WHERE priority <= 4
AND priority > (SELECT priority FROM categories WHERE category = 'Category2')
) AS x
WHERE x.cat_id = categories.cat_id
)
WHERE cat_id IN
(SELECT cat_id
FROM categories
WHERE priority <= 4
AND priority >= (SELECT priority FROM categories WHERE category = 'Category2')
);
The ultimate answer would combine these queries in such a way that it would work regardless of whether the named category moved up or down (and would do nothing if the category was nominated to stay in the same place). While I have no doubt that such a query could be developed, I don't think it is worth the effort involved. You could write a stored procedure to take the category name and the new priority for the category, and it would execute the requisite UPDATE statement. This could easily be driven from a UI (web browser or more closely coupled). What you've got above is complicated enough, but is explicable. And 'explicable' means you have a chance of it being 'maintainable' too.
Upvotes: 2