Related
Okay guys, i am trying to rephrase my question. Lets see how try 2 goes :)
I have a artisan command to calculate the total earnings for every user.
Every day a User gets earnings assigned. But sometime, how should I describe it, he made the earnings with stuff from another user.
And thats the assert_user_id column. If a user (user 2) made earnings with stuff from another user (user 1), these earnings should be added to the earnings of user 1 in this scenario.
id, user_id, earnings, asset_user_id
1, 1, 15, null
2, 1, 43, null
3, 1, 49, null
4, 2, 32, 1
5, 2, 25, 1
6, 2, 12, null
(In this case, user 2 is making almost all of his money with content from user 1... not very nice of him, thats why the money)
The Artisan commands gets all users. and goes through them with a foreach.
inside the foreach we call a method on the user model
return $this->sum('earnings');
remember, I am inside the collection of user 1.
And now I want to sum all earnings where asset_user_id = 1. The problem, the collection persist only the rows of user 1.
Now the question:
How can i access all other rows from inside a collection of a user. Yeha i could just do User:all() within my function in my model. But i don't think this would be SOLID code. So how do i do it the right way?
What you will probably want to do is iterate over the collection with foreach and get the sums yourself.
$results = Result::all();
$sum1 = 0;
$sum2 = 0;
foreach($results as $result) {
if($result->creator_id == 1 && $result->status = 'r') {
$sum1 += (int)$result->earnings;
}
if($result->asset_creator_id == 1 && $result->status = 'r') {
$sum2 += (int)$result->earnings;
}
}
There is also a sum method in the Collection class which might be a bit easier. Just send it a callback which returns the value you are looking for. We would have to do it twice though, once for each sum you are looking for.
$sum1 = $results->sum(function($result)
{
if($result->creator_id == '1' && $result->status = 'r') {
return $result->earnings;
}
});
$sum2 = $results->sum(function($result)
{
if($result->asset_creator_id == '1' && $result->status = 'r') {
return $result->earnings;
}
});
echo "Creator Earnings: ".$sum1;
echo "Asset Creator Earnings: ".$sum2;
Sorting, counting, summing over collection would be always worse solution than doing it with db query.
Anyway your question is messy, you ask for status=r, write total for status=f, so just a guess, to show you how you can accomplish that with Eloquent features:
public function getEarningsForAssetCreator($id)
{
return $this->where('asset_creator_id', $id)
->where('status', 'f')
->sum('earnings');
}
I've got a website on which I want to display items. These items are stored in a database in the following format:
ID Item Active
1 My item 0
2 My item 1
7 My item 1
8 My item 1
10 My item 0
Note here that the IDs are not necessarily evenly spaced. Each item is either active(1) or inactive(0). I want 3 items to be active at a time and every time my script is called I want the next item to become active and the oldest item to become inactive like so:
ID Item Active
1 My item 0
2 My item 0
7 My item 1
8 My item 1
10 My item 1
and so:
ID Item Active
1 My item 1
2 My item 0
7 My item 0
8 My item 1
10 My item 1
I'm currently struggling with the algorithm to consider the third case above. I can't just pick the highest ID that's active and move to the next item and set that active and at the same time pick the lowest one that's active and make it inactive.
Here's my code so far:
{
for ($i=0;$i<sizeof($videos);$i++)
{
echo $i."]";
if ($videos[$i]->active == 1)
{
if (!isset($first_active))
{
echo "mooh";
echo "[";
echo $first_active = $i;
echo "]";
}
if ( ($i < (sizeof($videos)-1)) && ($videos[$i+1]->active == 0) )
{
$videos[$i+1]->active = 1;
$videos[$i+1]->update();
echo "#".$first_active."|".$videos[$first_active]->id()."#";
$videos[$first_active]->active = 0;
$videos[$first_active]->update();
$first_active = null;
echo "|".$videos[$i+1]->id();
break;
}
elseif ($i == (sizeof($videos)-1))
{
$videos[0]->active = 1;
$videos[0]->update();
$videos[$first_active]->active = 0;
$videos[$first_active]->update();
$first_active = null;
echo "|".$videos[0]->id();
break;
}
}
}
}
This works until I get to the end, e.g. ID 10. It then correctly makes ID 1 active. In the next call, it makes ID 7 active and ID 1 inactive.
Any idea how I can 1) fix my code, 2) tackle this problem smarter?
Sounds to me like you actually need to store the last activation date instead of a simple on/off flag.
You can use two database queries, one to get the full list in order of ID, and another that specifically only gets the first 3 entries sorted by active date. Loop through your full list to get all the entries in order, then test the ID of each one with the ID of your 3 active to test if it is currently active. Then, setting a new item active is as simple as just updating the date/time of the active column on the item you want.
If you really want to keep the active on/off flag, then do so, just make another column with a last active date/time.
NOTE: **Please read all other related questions:**
Here is my first and second attempts at asking this question:
Efficient way to determine the outcome of test matrix
Would cartesian product be the best approach for this
Here is the problem:
I have several ( like 20 ) Boolean validations ( true / false )
All Boolean validations as a whole also have a validation Result
I'm trying to find the best solution to test all the validations and also the validation result. I was looking into a Matrix to hold all possible combinations but that might be an overkill.
Here is an example ( 1 - 20 ):
test_1 = Has 30 Kills
test_2 = Has Found Map 1
test_3 = Has Mastered Level 1
test_4 = Has Achieved Grunt Status
test_5 = Has Assault weapon
test_6 = Has Knife
test_7 = Has Grenade
test_x = Etc...
So when the Player has all these validations as TRUE I can then give a level result
if test_1, test_2, test_3 ( any combination of the three ): level = green
All combinations are ( 15 ):
test_1
test_2
test_3
test_1, test_2
test_1, test_3
test_2, test_1 ( duplicate can skip this )
test_2, test_3
test_3, test_1 ( duplicate can skip this )
test_3, test_2 ( duplicate can skip this )
test_1, test_2, test_3
test_1, test_3, test_2 ( duplicate can skip this )
test_2, test_1, test_3 ( duplicate can skip this )
test_2, test_3, test_1 ( duplicate can skip this )
test_3, test_1, test_2 ( duplicate can skip this )
test_3, test_2, test_1 ( duplicate can skip this )
So unique combinations are ( 7 instead of 15 ):
test_1
test_2
test_3
test_1, test_2
test_1, test_3
test_2, test_3
test_1, test_2, test_3
Now I'm trying to find the best possible solution to find unique combinations for all 20 validations and come up with a level validation from that matrix.
UPDATE:
Also I need to find only TRUE Combinations so you might read the Unique Combinations like this:
test_1
test_2
test_3
test_1, test_2
test_1, test_3
test_2, test_3
test_1, test_2, test_3
Boolean Value Results from Validation Tests
TRUE, FALSE, FALSE
FALSE, TRUE, FALSE
FALSE, FALSE, TRUE
TRUE, TRUE, FALSE
TRUE, FALSE, TRUE
FALSE, TRUE, TRUE
TRUE, TRUE, TRUE
So any of these combinations would be a GREEN level.
Also I need to know the order of the test validations as well as the matrix order to compare for level assignment. So for GREEN level I only need the validation result combination matrix for test 1, 2 and 3. So I could ignore tests 4 - 20
UPDATE #2:
I know this looks like a simple OR condition but I wanted to take out the combination logic to set the level into a matrix. I could use the matrix of combinations to determine the level logic without having to code additional or modify current logic in the code itself. I wanted to just compare the validations results for a given set of tests and assign a level to those results. Different permutations of the validation combinations would result in different level assignments.
I understand that I could add the combination logic in the code itself, but as this logic looks to be very volatile and thought this might offer a more flexible solution.
Suggestions?
(removed my two previous answers for clarity)
After your last edit, instead of answering directly, I would like first to be sure to 100% understand the "level detection algorithm" you want.
If I understand well, you would like to define/maintain a simple configuration structure telling which tests give which level.
e.g. with an associative array:
array(
'green' => array('test1', 'test2', 'test3'),
'orange' => array('test2', 'test3', 'test5')
...
);
With the meaning: if one or more of the tests in the list are satisfied, assign that level (array key) to the player. Such logic could easily cover quite a lot of combinations, and would avoid handling a huge matrix.
Maybe you want to extend the logic to tell, for example, that at least N tests among the test list are satisfied.
array(
'green' => array(
'tests' => array('test1', 'test2', 'test3'),
'nb_required' => 2
),
...
);
Is that what you want?
BTW, why don't you use a classic XP/level up system? :-p
Not quite to answer your question, but it seems you're missing something.
Lets say you have twenty tests, labeled 1 though n. Decide for test n whether you want to validate for it, or not. Either you include the test for validation, or you don't. That's two choices for every test. Proceed through (n-1) until you have no more tests. For n=20, that's 2^20 = 1048576 possible combinations of tests (including one combination where you don't select any tests), which means 1048576 results. Now I still don't understand what you mean by "level of validation", but I have to wonder why you'd need that many combinations of tests in the first place.
Edit: Well, a test matrix (so to speak) could work.... but you'd still have to generate the matrix. You're likely not going to hand code all 1048576 combinations. But supposing you've already created the mapping, you just have a zero indexed array as a look up table with 1048576 values that you desire and you're done! All you would need to map the test results to the matrix would be assigning each test a binary digit (test1 would be the one's place, test2 would be the 2's place, etc.)
What I suspect you really want is a quick way to generate the mapping given some broader rules you might encode in php... but here's funny part about that. If you had some code that generated the matrix for any and every set of tests, the matrix is then superfluous; Your code is essentially a compressed representation for the matrix. The only point in using one over the other is whether or not one would be faster than the other.
It also seems you really don't care about all 1048576 combinations. I suspect it might benefit you to partition your tests into their own set of tests (one for red, one for blue, etc). For instance, if you partition tests into groups of 5, and there is, oh, 3 different possibilities (instead of 16) for each group, you're only working with (3 different results per group)^(4 groups) = 81 unique results. Eighty one unique results is much more manageable than over a million.
It seems you may also need different partitions for different independent things. It also may not matter even which tests result in true as long as a certain number of them are true, that is "goal: 3 out of 5 blue objectives met" "goal: 7 out of 10 red and blue objectives met" or whatever. Those tests would have to be accessed independently and they wouldn't necessarily be multiplicative with the results of the other tests-- there will never be 7 out of 10 red and blue objectives met if none of your blue objectives are met (I'll admit, this is hard to explain further without an example).
So, really, there's no quick answer. Either you deal with all and every one of the 1048576 combinations individually, or you create and encode some sort of generic grouping scheme for your tests to drastically cut down on combinations. You can certainly create your full matrix scheme by said scheme, and maybe even dynamically generate your full 1048576 element matrix for a given set of combinations. But you cannot burn a whole candle by only burning half of it.
Edit II (and III):
I'm going to make one more guess. The levels you've proposed seem to be related to the number of objectives completed per each (and previous batch). So encode your results in to a string of ("T", and "F") and count the "F"s for each batch and determine from there. If the count of Fs is the number of characters in the string, no objective of the batch is completed, if the number of Fs is zero, all objectives of the batch are completed.
Lets say the batch after blue is purple. Could somebody achieve a goal in purple without completing all the green batch? If so, you have another color to assign for that possibility; if not, then, I would assume that these levels are ordered (orange between green and blue, maybe "brown" or something between blue and purple--you can tell I pulled this out of thin air-- and it should be fairly simple to cascade through these counts to determine the current "level".
If there is no ordering, then it's quite analogous to the situation I mentioned above: Each group has a result { "no objectives completed", "some objectives completed", "all objectives completed"}. There are x groups which means 3^x possible results. You should be basing your level matrix on these results, not the results of the tests previously mentioned.
Introduction
You can easy get combinations like this :
echo "<pre>";
$test = ["test_1","test_2","test_3"];
// Get Combination
$return = uniqueCombination($test);
//Sort
sort($return);
//Pretty Print
print_r(array_map(function($v){ return implode(",", $v); }, $return));
function uniqueCombination($in, $minLength = 1, $max = 10) {
$count = count($in);
$members = pow(2, $count);
$return = array();
for($i = 0; $i < $members; $i ++) {
$b = sprintf("%0" . $count . "b", $i);
$out = array();
for($j = 0; $j < $count; $j ++)
$b{$j} == '1' and $out[] = $in[$j];
count($out) >= $minLength && count($out) <= $max and $return[] = $out;
}
return $return;
}
Output
Array
(
[0] => test_1
[1] => test_2
[2] => test_3
[3] => test_1,test_2
[4] => test_1,test_3
[5] => test_2,test_3
[6] => test_1,test_2,test_3
)
The Problem
They are about 1,048,576 combination and i believe this is not the kind of array you want i would suggest a condition based combination rather than all possible combination
Example
// Game Conditions
$game = new Game();
$game->addCondition(new Condition(new Level(1), new Kill(30)));
$game->addCondition(new Condition(new Level(2), new Map(1), new Kill(10)));
$game->addCondition(new Condition(new Level(3), new Grunt(10)));
$game->addCondition(new Condition(new Level(4), new Knife(1), new Ak47(1)));
$game->addCondition(new Condition(new Level(5), new Grenade(1), new Combo(7)));
$game->addCondition(new Condition(new Level(6), new Kill(100), new Blow(10), new Stab(10)));
$game->addCondition(new Condition(new Level(7), new Herb(10), new Medipack(1), new Map(1), new Artwork(1)));
$game->addCondition(new Condition(new Level(8), new Grenade(20),new Artwork(5)));
// User Starts Game
$user = new User($game);
$user->task(new Map(1));
$user->task(new Herb(5));
$user->task(new Kill(10));
$user->task(new Kill(10));
$user->task(new Herb(10));
$user->task(new Kill(10));
$user->task(new Kill(10));
$user->task(new Ak47(1));
$user->task(new Knife(1));
$user->task(new Map(1));
$user->task(new Grunt(17));
$user->task(new Kill(60));
$user->task(new Combo(1));
$user->task(new Kill(40));
$user->task(new Medipack(1));
$user->task(new Artwork(1));
$user->task(new Grenade(1));
$user->task(new Combo(10));
$user->task(new Blow(10));
$user->task(new Stab(5));
$user->task(new Blow(10));
$user->task(new Stab(5));
$user->task(new Stab(5));
printf("\n<b>Total Point %s",number_format($user->getPoint(),0));
Output
+Task Map Added (1)
+Task Herb Added (5)
+Task Kill Added (10)
^Task Kill Updated (20)
^Task Herb Updated (15)
^Task Kill Updated (30)
*Level 1 Completed*
*Level 2 Completed*
^Task Kill Updated (40)
+Task Ak47 Added (1)
+Task Knife Added (1)
^Task Map Updated (2)
+Task Grunt Added (17)
*Level 3 Completed*
*Level 4 Completed*
^Task Kill Updated (100)
+Task Combo Added (1)
^Task Kill Updated (140)
+Task Medipack Added (1)
+Task Artwork Added (1)
+Task Grenade Added (1)
^Task Combo Updated (11)
*Level 5 Completed*
+Task Blow Added (10)
+Task Stab Added (5)
^Task Blow Updated (20)
^Task Stab Updated (10)
*Level 6 Completed*
*Level 7 Completed*
^Task Stab Updated (15)
<b>Total Point 1,280</b>
Classes Used
class Task {
private $no;
function __construct($no = 1) {
$this->no = $no;
}
function getNo() {
return $this->no;
}
function getName() {
return get_called_class();
}
function merge(Task $task) {
$this->no += $task->getNo();
return $this;
}
}
class User {
private $game;
private $point;
private $tasks = array();
function __construct(Game $game) {
$this->game = $game;
}
function getPoint() {
return $this->point;
}
function getTask() {
return $this->tasks;
}
function task(Task $task) {
if (isset($this->tasks[$task->getName()])) {
$this->tasks[$task->getName()]->merge($task);
printf("^Task %s \tUpdated (%s)\n", $this->tasks[$task->getName()]->getName(), $this->tasks[$task->getName()]->getNo());
} else {
printf("+Task %s \tAdded (%s)\n", $task->getName(), $task->getNo());
$this->tasks[$task->getName()] = $task;
}
$this->point += $task->getNo() * $task->d;
$this->game->notify($this);
}
}
class Condition {
private $task = array();
private $status = false;
function __construct(Level $level) {
$this->level = $level;
$tasks = func_get_args();
array_shift($tasks);
$this->task = new SplObjectStorage($tasks);
foreach ( $tasks as $task )
$this->task->attach($task);
}
function update(Game $game, User $user) {
if ($this->status)
return;
$n = 0;
foreach ( $this->task as $cTask ) {
foreach ( $user->getTask() as $task ) {
if ($cTask->getName() == $task->getName()) {
if ($task->getNo() >= $cTask->getNo())
$n ++;
}
}
}
if ($n === count($this->task) && ($game->getLevel()->getNo() + 1) == $this->level->getNo()) {
$this->status = true;
$game->setLevel($this->level);
printf("\n*Level %d Completed* \n\n", $this->level->getNo());
}
}
function getStatus() {
return $this->status;
}
}
class Game {
private $taskCondition;
private $level;
public function __construct() {
$this->taskCondition = new SplObjectStorage();
$this->level = new Level(0);
}
function setLevel(Level $level) {
$this->level = $level;
}
function getLevel() {
return $this->level;
}
function addCondition($condition) {
$this->taskCondition->attach($condition);
}
public function notify($user) {
foreach ( $this->taskCondition as $conditions ) {
if ($conditions->getStatus() === true) {
// detached completed condition
$this->taskCondition->detach($conditions);
continue;
}
$conditions->update($this, $user);
}
}
public function hasCondition() {
return count($this->taskCondition);
}
}
class Level extends Task{}
class Action extends Task{};
class Weporn extends Task{};
class Skill extends Task{};
class Tresure extends Task{};
class Medicine extends Task{};
class Kill extends Action{public $d = 5 ;};
class Blow extends Action{public $d = 7 ;};
class Stab extends Action{public $d = 10 ;};
class Map extends Tresure{public $d = 10 ;};
class Artwork extends Tresure{public $d = 20 ;};
class Knife extends Weporn{public $d = 5 ;};
class Grenade extends Weporn{public $d = 10 ;};
class Ak47 extends Weporn{public $d = 10 ;};
class Jump extends Skill{public $d = 2 ;};
class Grunt extends Skill{public $d = 4 ;};
class Combo extends Skill{public $d = 7 ;};
class Medipack extends Medicine{public $d = 5 ;};
class Herb extends Medicine{public $d = 5 ;};
Simple Online Demo
I have found the graphic display of your question as you mentioned.
|0|1|2|3|4|5|6|7|8|9|
|1|T|T|T|T|T|T|T|T|T|
|2|F|T|T|T|T|T|T|T|T|
|3|F|F|T|T|T|T|T|T|T|
|4|F|F|F|T|T|T|T|T|T|
|5|F|F|F|F|T|T|T|T|T|
|6|F|F|F|F|F|T|T|T|T|
|7|F|F|F|F|F|F|T|T|T|=>[7,9] if this is the answer
|8|F|F|F|F|F|F|F|T|T|
|9|F|F|F|F|F|F|F|F|T|
In my opinion you should check the conditions in reverse diagonal order, then left to right.
That means
first check [9,9]
if it fails check [8,8]
if this fails check [7,7]
if it gives true check [7,8]
if this gives false check [7,9] this must be the answer and the simple shortcut,
This method will reduce over all process time.
gurus!
First of all, I've spent half a day googlin'n'stackoverflowing but couldn't find a solution. This is my first time working with recursions. Hope someone can help.
I have a MySQL table, this is kind of referal system:
Table 'users'
ID SPONSORID
---------
1 2
2 1
3 1
4 1
... ...
There are several things to keep in mind:
Users 1 and 2 are sponsors/referals of each other.
Each user can have unlimited number of referals.
Each user's referal becomes a sponsor as well and can also have unlimited number of referals
Depth is unlimited.
The task is to recursively build a tree of a single "team", like:
User 1
User 2
User 1
User 5
...
....
User 3
User 295
User 356
....
User 4
and so on...
Here's what I'm trying to do:
$team = Array();
function build_team( $userID, $team ){
if ( !in_array($userID, $team ) :
// get 1st level of referals
// returns associative array ('id', 'login', 'sponsorid')
$referals = get_user_referals( $userID );
for ( $i=0; $i<count($referals); $i++ ) :
$team[] = $referals[$i];
build_team( $referals[$i]['id'] );
endfor;
endif;
}
And also tried putting that IF inside the FOR block, but it still goes in infinite loop. As I understand I need a condition to quit recursion when there's no depth level but I can't understand how to calculate/determine it. Any suggestions?
Keep users ids, whom are already "built", somewhere. As you said yourself - "Users 1 and 2 are sponsors/referals of each other.", so there is your infinite loop.
Something like
if (!in_array($userId, $already_looped_users){
//continue loop...
}
Adding some code:
$team = Array();
function build_team( $userID, $team ){
if ( !in_array($userID, $team ) :
// get 1st level of referals
// returns associative array ('id', 'login', 'sponsorid')
$referals = get_user_referals( $userID );
if (count($referals) > 0){ //added this line
for ( $i=0; $i<count($referals); $i++ ) :
$team[] = $referals[$i];
$team[$referals[$i] = build_team( $referals[$i]['id'], $team ); // i've edited this line
endfor;
return $team;
}
endif;
}
This is for MySQL and PHP
I have a table that contains the following columns:
navigation_id (unsigned int primary key)
navigation_category (unsigned int)
navigation_path (varchar (256))
navigation_is_active (bool)
navigation_store_id (unsigned int index)
Data will be filled like:
1, 32, "4/32/", 1, 32
2, 33, "4/32/33/", 1, 32
3, 34, "4/32/33/34/", 1, 32
4, 35, "4/32/33/35/", 1, 32
5, 36, "4/32/33/36/", 1, 32
6, 37, "4/37/", 1, 32
... another group that is under the "4/37" node
... and so on
So this will represent a tree like structure. My goal is to write a SQL query that, given the store ID of 32 and category ID of 33, will return
First, a group of elements that are the parents of the category 33 (in this case 4 and 32)
Then, a group of elements that are a child of category 33 (in this case 34, 35, and 36)
Then the rest of the "root" categories under category 4 (in this case 37).
So the following query will return the correct results:
SELECT * FROM navigation
WHERE navigation_store_id = 32
AND (navigation_category IN (4, 32)
OR navigation_path LIKE "4/32/33/%/"
OR (navigation_path LIKE "4/%/"
AND navigation_category <> 32))
My problem is that I want to order the "groups" of categories in the order listed above (parents of 33 first, children of 33 second, and parents of the root node last). So if they meet the first condition, order them first, if they meet the second condition order them second and if they meet the third (and fourth) condition order them last.
You can see an example of how the category structure works at this site:
www.eanacortes.net
You may notice that it's fairly slow. The current way I am doing this I am using magento's original category table and executing three particularly slow queries on it; then putting the results together in PHP. Using this new table I am solving another issue that I have with magento but would also like to improve my performance at the same time. The best way I see this being accomplished is putting all three queries together and having PHP work less by having the results sorted properly.
Thanks
EDIT
Alright, it works great now. Cut it down from 4 seconds down to 500 MS. Great speed now :)
Here is my code in the Colleciton class:
function addCategoryFilter($cat)
{
$path = $cat->getPath();
$select = $this->getSelect();
$id = $cat->getId();
$root = Mage::app()->getStore()->getRootCategoryId();
$commaPath = implode(", ", explode("/", $path));
$where = new Zend_Db_Expr(
"(navigation_category IN ({$commaPath})
OR navigation_parent = {$id}
OR (navigation_parent = {$root}
AND navigation_category <> {$cat->getId()}))");
$order = new Zend_Db_Expr("
CASE
WHEN navigation_category IN ({$commaPath}) THEN 1
WHEN navigation_parent = {$id} THEN 2
ELSE 3
END, LENGTH(navigation_path), navigation_name");
$select->where($where)->order($order);
return $this;
}
Then I consume it with the following code found in my Category block:
// get our data
$navigation = Mage::getModel("navigation/navigation")->getCollection();
$navigation->
addStoreFilter(Mage::app()->getStore()->getId())->
addCategoryFilter($currentCat);
// put it in an array
$node = &$tree;
$navArray = array();
foreach ($navigation as $cat)
{
$navArray[] = $cat;
}
$navCount = count($navArray);
$i = 0;
// skip passed the root category
for (; $i < $navCount; $i++)
{
if ($navArray[$i]->getNavigationCategory() == $root)
{
$i++;
break;
}
}
// add the parents of the current category
for (; $i < $navCount; $i++)
{
$cat = $navArray[$i];
$node[] = array("cat" => $cat, "children" => array(),
"selected" => ($cat->getNavigationCategory() == $currentCat->getId()));
$node = &$node[0]["children"];
if ($cat->getNavigationCategory() == $currentCat->getId())
{
$i++;
break;
}
}
// add the children of the current category
for (; $i < $navCount; $i++)
{
$cat = $navArray[$i];
$path = explode("/", $cat->getNavigationPath());
if ($path[count($path) - 3] != $currentCat->getId())
{
break;
}
$node[] = array("cat" => $cat, "children" => array(),
"selected" => ($cat->getNavigationCategory() == $currentCat->getId()));
}
// add the children of the root category
for (; $i < $navCount; $i++)
{
$cat = $navArray[$i];
$tree[] = array("cat" => $cat, "children" => array(),
"selected" => ($cat->getNavigationCategory() == $currentCat->getId()));
}
return $tree;
If I could accept two answers I would accept the first and last one, and if I could accept an answer as "interesting/useful" I would do that with the second.
:)
A CASE expression should do the trick.
SELECT * FROM navigation
WHERE navigation_store_id = 32
AND (navigation_category IN (4, 32)
OR navigation_path LIKE "4/32/33/%/"
OR (navigation_path LIKE "4/%/"
AND navigation_category <> 32))
ORDER BY
CASE
WHEN navigation_category IN (4, 32) THEN 1
WHEN navigation_path LIKE "4/32/33/%/" THEN 2
ELSE 3
END, navigation_path
Try an additional derived column like "weight":
(untested)
(IF(criteriaA,1,0)) + (IF(criteriaB,1,0)) ... AS weight
....
ORDER BY weight
Each criteria increases the "weight" of the sort.
You could also set the weights distinctly by nesting IFs and giving the groups a particular integer to sort by like:
IF(criteriaA,0, IF(criteriaB,1, IF ... )) AS weight
Does MySQL have the UNION SQL keyword for combining queries? Your three queries have mainly non-overlapping criteria, so I suspect it's best to leave them as essentially separate queries, but combine them using UNION or UNION ALL. This will save 2 DB round-trips, and possibly make it easier for MySQL's query planner to "see" the best way to find each set of rows is.
By the way, your strategy of representing the tree by storing paths from root to tip is easy to follow but rather inefficient whenever you need to use a WHERE clause of the form navigation_path like '%XYZ' -- on all DBs I've seen, LIKE conditions must start with a non-wildcard to enable use of an index on that column. (In your example code snippet, you would need such a clause if you didn't already know that the root category was 4 (How did you know that by the way? From a separate, earlier query?))
How often do your categories change? If they don't change often, you can represent your tree using the "nested sets" method, described here, which enables much faster queries on things like "What categories are descendants/ancestors of a given category".