SilverStripe get by id in exact order - php

Suppose you have an array of keys
$key_list = array(3, 6, 2);
And you want to retrieve records from a certain table, using these keys as identifiers (WHERE ID = id_from_key_list)
Foo::get()->byIDs($key_list);
This returns the rows with the ID's that match those in $key_list (3, 6 and 2) but not in that order.
How do we maintain the same order when retrieving these items?

What you might need to do is to run a foreah loop of the IDs and push each Foo Object into an ArrayList
$aFooList = ArrayList::create();
foreach ($key_list as $key_list_id){
$oFoo = Foo::get()->byID($key_list_id);
$aFooList->push($oFoo);
}
return $aFooList;

Related

Laravel - How to pass different values for pivot data table in sync() function

My form sends a $request->assets that I am able to sync using the following:
$user->assets()->sync($request->assets === null ? [] : $syncData);
The complexity (not a problem) arises when I try to retrieve values from a serial number array that is sent back as $request->serialnumber
I have set up the serial numbers in such a way that the serial number array index corresponds to my id column in the assets table. E.g. if Mobile has a value of 1 in the database, it is located in $request->serialnumber[1], and for something that would have an id of 2, would be placed in $request->serialnumber[2] and so on.
I have done the following so far, in order to insert the correct serial numbers for the correct asset() relationship:
$serialData = [];
$pivotData = [];
$syncData = [];
//for every assigned asset, build an array of their serial numbers from the serial number array...
if(isset($request->assets))
{
for($i = 0; $i<count($request->assets);$i++)
$serialData[$i] = $request->serialnumber[$i];
}
//if some serials were set, use them for pivot data
if(count($serialData)>0)
{
$filledArray = array_fill_keys($serialData,"serialnumber");
$pivotData = array_flip($filledArray);
}
$syncData = array_combine($request->assets, $pivotData);
I know its a long drawn out way, so I'm wondering if there's an easier way to do this?

Generate a PHP array with 100 tickets and a random number to compare

I am new to symfony and I am trying to make a function that generates a random number and then I want to check this random number in an array of prices. Let's say there are 100 tickets so 100 items in the array. 50 of them are a price "foo" 30 "bar" and 0 "win nothing".
My goals is to populate this array at random so it should look like this:
array(
1=>foo,
2=>foo,
3=>bar,
4=>nothing,
...
)
EDIT
here is what i've tried but doesn't seem to be working. The array is being filled with only the last fill
$prices = array_fill(0, 99, '10');
$prices = array_fill(100, 139, '100');
$prices = array_fill(139, 149, '200');
$prices = array_fill(149, 150, 'reis');
($prices);
I have no idea how to populate my array at random and could use all the help in the world
Any help would be realy awesome!
Thanks in advance!
Your problem can be broken up into two simple parts. First, you need to create an array with an arbitrary number of values, which you could do with a function like this, as per your requirements described above.
function createTickets(Array $items, $totalItems) {
$arr = [];
// populate each item with $quantity
foreach($items as $item => $quantity) {
$arr = array_merge($arr, array_fill(0, $quantity, $item));
}
// fill in the rest with 'nothhing'
$arr = array_merge($arr, array_fill(0, $totalItems - array_sum($items), 'nothing'));
// return the final result
return $arr;
}
So, to create an array of 100 items, with 50 foo and 30 bar, and the rest are nothing, you would do something like this.
$tickets = createTickets(['foo' => 50, 'bar' => 30], 100);
Second, you need to select items at random from the array you created.
The actual order of the elements in the array doesn't matter. You can use something like shuffle to randomize the order of elements in the array, but that's not necessary, because you can always select items from the array at random using array_rand, for example.
// Randomly select an item from the array
$someItem = $tickets[array_rand($tickets)];
Becuase you already know how many items you're creating in the array, you can always supply the amount of random items to extract to array_rand.
// Select 10 items at random
foreach(array_rand($tickets, 10) as $key) {
echo $tickets[$key], "\n"; // do something with item here
}
If you prefer to just shuffle the array because you intend to consume the entire array at random you could just do that instead.
shuffle($tickets);
foreach($tickets as $ticket) {
// now each item is at random
}

Symfony 1.4 + Pager : Ignore order array

I have a symfony problem: The functionally works good, but this does not work the way I want.
$res = array("4","2","1","3"); // LIST ID (a.id)
$paginas = new sfDoctrinePager('TbArticle', 2);
$paginas->setQuery(Doctrine::getTable('TbArticle')->createQuery('a')->where('a.ifactive = 1')->andWhere('a.dirimage=1')->andWhere('a.stock<>0')->whereIn("a.id", $res));
$paginas->setPage($page);
$paginas->init();
It works okay, but when I call getResults(), the array order is incorrect. For instance, this sort returns: 1,2,3,4. And I like to get: 4, 2, 1, 3 ($res)
Can you help me?
Unfortubately this cannot be done with the query.
The MySQL queries can be returned ordered using the ORDER BY clause in ascending or descending order. Elements in your array use none. When you pass the array as a parameter for the WHERE IN clause MySQL doesn't care about the order of the elements as you can see.
Fortunately there is a solution :)
First you will have to use Doctrine's ability to create a table of results indexed with what you want. Use this:
Doctrine::getTable('TbArticle')->createQuery('a INDEX BY id')->...;
This will return an array of results where the array keys are the id's of the rows. Then you can rearange the results array to match your $res (assuming that $rows has the rows returned by Doctrine):
foreach ($res as $i) {
$new_array[] = $rows[$i];
}
The tricky part is to make it work with the paginator. But I'm sure you can do that as well (try to retrieve the results from the paginator and rearange them before displaying).

ORM Mapping two tables with PHP

Current situation
I have two tables in my database, one for posts, and one for ratings. These are linked with a relation in the MySQL so that one post may have 0, 1 or multiple ratings, but one rating can only be applied to one post.
When I fetch a list of posts, I also want to get ratings, but without having to make a separate call to the database for each post in the foreach loop.
To do this I have attempted to use an SQL query to fetch all posts with a LEFT JOIN on ratings so that it will return a result like this:
statusId|statusBody|rating
-----------------------------
1, post1, 0
1, post1, 1
2, post2, 0
3, post3, 1
3, post3, 1
The SQL works fine, and I get the data I ask for.
Ideally what I am trying to achieve now is to turn this table into a collection of objects, with each object storing the post information as well as a value depending on it's total ratings.
After using PDO to return the data result, this is the code I am using to map the data:
Code Logic
The logic of my code goes like this:
Get all statuses joined with ratings table
Create empty output array
Loop through PDO result
{
Create loop specific temp array
Push first row of result into temp array
Remove row from PDO result
Loop through PDO result for objects with matching statusId
{
If row matches statusId, add to temp buffer and remove from PDO result
}
Take first row of buffer and create status object
Loop through objects in temp array to calculate ratings and add onto above status object
Clear temp buffer
Add status object to output array
}
return output array
Actual Code
try
{
$result = $pdo->query($sql);
//if($result == false) return false;
$statuses = $result->fetchAll(PDO::FETCH_CLASS, 'status');
}
catch (PDOException $e)
{
return FALSE;
}
if (!$result) {
return FALSE;
}
//create empty output array to be filled up
$status_output = array();
//loop through all status
foreach($statuses as $s1key => $s1value)
{
//initialise temporary array;
$status_temp_buffer = array();
//create temp array for storing status with same ID in and add first row
array_push($status_temp_buffer, $s1value);
//remove from primary array
unset($statuses[$s1key]);
//loop through array for matching entries
foreach($statuses as $s2key => $s2value)
{
//if statusId matches original, add to array;
if($s2value->statusId == $s1value->statusId)
{
//add status to temp array
array_push($status_temp_buffer, $s2value);
//remove from primary array
unset($statuses[$s2key]);
}
//stop foreach if statusId can no longer be found
break;
}
//create new status object from data;
$statObj = $status_temp_buffer[0];
//loop through temp array to get all ratings
foreach($status_temp_buffer as $sr)
{
//check if status has a rating
if($sr->rating != NULL)
{
//if rating is positive...
if($sr->rating == 1)
{
//add one point to positive ratings
$statObj->totalPositiveRatings++;
}
//regardless add one point to total ratings
$statObj->totalAllRatings++;
}
}
//clear temporary array
$status_temp_buffer = NULL;
//add object to output array
array_push($status_output, $statObj);
}
Problem
The problem I am coming up against with this code is that although the ratings are fine, and it correctly calculates the ratings total for each post, it still shows duplicates where a post has more than one rating.
Any help with this would be greatly appreciated,
Thanks
As i understood it, the goal is to get the total rating of each Post entry. Instead of manually looping over each and every rating, there are two other path you could take:
compute the total in the query:
SELECT SUM(rating) AS total , .. FROM Posts LEFT JOIN .... GROUP BY statusID
You will receive a list of Post entries, each already with total rating calculated. This is a very good solution if you have a lot of writes to to the Ratings table, and much less reads.
the other way is to break the table normalization, but to increase read performance. What you would have to do is to add another column in the Posts table: total_rating. And have an TRIGGER on INSERT in the Ratings table, which changes the Posts.total_rating accordingly.
This way has a benefit of simplifying the request of Posts. At the same time Ratings table can now be use to ensure that total_rating has been calculated correctly, or to recalculate the value, if there are some large changes in the ratings: like banning of user, which results in removing all ratings made by this user.

How to create php array from mysql result, perform calculation, resort array, present results

I have a photo website on which I am trying to perform a query against a MySQL database. The query is against a concatenated field of 'title' and 'keyword' called 'title_keyword'.
I want to take the search results and sort them by a newly formed variable called 'sort_priority' which is checking to see if the search word is in the 'title' field. If it is in the 'title' field then I want to assign a value of 1 and if not in the title field then a value of 2. The resulting array will be sorted by 'sort_priority' and output to the screen.
Here is the logic I am using with PHP and MySQL:
1) Query the MySQL database and assign variables. (This works just fine)
2) Take the results, assign each field to a variable, create a new variable that performs a calculation on one of the variables returned
$data_array=array();
// get each row
while($row = mysql_fetch_array($result))
{
//get data
$image_id = "{$row['image_id']}";
$title = "{$row['title']}";
$imageurl = "{$row['imageurl']}";
// Create sort_priority to identify if search word is in title field.
//If it is then set to 1 to force this higher in the result list after sorting
$sort_priority = 2;
if(stristr($title,$search))
{ $sort_priority = 1;}
Everything above this point works. Now for the part I'm stumped on. How to create and add data to the array and then sort on my new $sort_priority variable.
Here is what I've written but it just doesn't work**
// Create array and sort by title then keyword (tk_sort)
$data_array = array(
'image_id' => $image_id,
'title' => $title,
'imageurl' => $imageurl,
'sort_priority' => $sort_priority);
// Obtain a list of columns
foreach ($data_array as $key => $row) {
$image_id[$key] = $row['image_id'];
$title[$key] = $row['atitle'];
$imageurl[$key] = $row['imageurl'];
$sort_priority[$key] = $row['sort_priority'];
}
// Sort the data with volume descending, edition ascending
// Add $data as the last parameter, to sort by the common key
array_multisort($sort_priority, SORT_ASC);
// end of array creation and sort
3) Output the newly sorted array to a table
Not sure how to get the data out of it. Do I have to use a loop or something?
You could just let MySQL do the majority of the work. This should work (haven't tried it myself):
SELECT CONCAT_WS('-', `title`, `keyword`) AS search_term,
IF( INSTR(`search_term`, 'your_search_value_here') > 0, 1, 2 ) AS priority_key,
`image_id`, `imageurl`
FROM table_name_here
ORDER BY `priority_key`;
HTH.

Categories