Laravel function belong in model or controller - php

I have a function which contains a query and a percentile calculation (below) on the query's results/collection.
This function is needed in the following way:
I have an Athlete model, with many Tests. Each Test also has many Results. For every result (displayed in a table), I need to display a calculated percentile (which is not stored). It's at this point (the table view) where I need the function to calculate the percentile for every result.
I have detailed the models here. The answer of that post also shows the query used.
If I stick that function in the Result model, it allows me easy access to it from the view in this manner {{$result->getThisRank($test->age, $an_athlete->gender, $result->battery_id, $result->score)}}
If I put it in a Controller, I would need some dynamic and complex way to call a route for each specific Result in the table. Not even sure how to do this.
Question: Is it correct to have this query and calculation in the Result model?
An earlier post here, seems to also go with model.
The calculation performed on the query collection:
for ($i = 1; $i < 100; $i++ ){
$rank = 0.0;
$p = 0.0;
$rank = $i/100 * ($count + 1);
$p = $rank;
//get integer and decimal for interpolation http://onlinestatbook.com/lms/introduction/percentiles.html
$intpart = floor($p);
$fraction = $p - $intpart;
//dd($intpart . ' '.$fraction);
if($fraction > 0){ //part of percentile formula - see article
//test for min array index to not be out of bound. Test negative, most used case.
if($intpart != 0){
$scoreLow = $all_scores[$intpart - 1];
if($intpart == $count){ //test for max array index to not go over bound
$scoreHigh = $all_scores[$intpart - 1];
} else{
$scoreHigh = $all_scores[$intpart];
}
} else{
$scoreLow = $all_scores[0];
$scoreHigh = $all_scores[$intpart];
}
//scoreLow and scoreHigh has been determined, now final step for decimal rank
$scoreHigh = $scoreHigh * 1.0;
$scoreLow = $scoreLow * 1.0;
$rank = ($fraction * ($scoreHigh - $scoreLow)) + $scoreLow;
} else{
$rank = ($all_scores[$intpart - 1] * 1.0);//no decimal rank, plain rank calculation
}
if($sortorder == 'asc'){
$rankings[$i.'th %'] = $rank;
}else{
$rankings[100 - $i.'th %'] = $rank;
}
//$rankings->add(['rank'.$i => $rank]);
}
//dd($rankings);
//reverse rankings
$rev_rankings = array_reverse($rankings);
if ($battery == 111){
dd($rev_rankings);
}
$view_rank = null;
foreach($rev_rankings as $key => $rank){
if($athlete_score == $rank){
$view_rank = $key;
break;
}
if($athlete_score > $rank){
$view_rank = $key;
break;
}
}
return($view_rank);
}
else{
return ('Not available');
}

The type of calculations you describe certainly belong in the domain (i.e. - model). Depending on how you're structuring your application you may place the function within an existing Eloquent model or even as part of a new entity which doesn't extend the ORM. Additionally, I would standardize the type of return value it provides, perhaps restricting it to numerical values or possibly numerical values as well as NULL. Then I would replace in the view (blade template) any NULL values, etc. with "Not available" as I notice at the bottom of your function.

Related

Sorting Steam Web Api GetPlayerSummaries response

I'm making class which will get data about teams - 5 Steam users basing on 32bit SteamIDs stored in database for each team. It's translated then to 64bit SteamID.
I need response in correct order, because there is specified captain of the team.
And here's the problem - GetPlayerSummaries always returns profiles in random order. I want these to be sorted like in url.
I've tried already sort() methods, but it seems not working, like I want to.
I've tried matching 'steamid' with this translated 64 bit SteamID like this:
$profile_get = json_decode(file_get_contents('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=mywebapikey&steamids='.$stmid64['capt'].','.$stmid64['p2'].','.$stmid64['p3'].','.$stmid64['p4'].','.$stmid64['p5']),true);
$profile_get = $profile_get['response'];
foreach($profile_get['players'] as $profile){
if($profile['steamid'] === $stmid64['capt']){
$profile_got = array(
0 => $profile
);
}
elseif($profile['steamid'] === $stmid64['p2']){
$profile_got[1] = $profile;
}
elseif($profile['steamid'] === $stmid64['p3']){
$profile_got[2] = $profile;
}
elseif($profile['steamid'] === $stmid64['p4']){
$profile_got[3] = $profile;
}
elseif($profile['steamid'] === $stmid64['p5']){
$profile_got[4] = $profile;
}
}
where $stmid64 is 64bit SteamID, but it obviously don't work :(
var_dump($profile_got[0]);
var_dump($profile_got[1]);
var_dump($profile_got[2]);
var_dump($profile_got[3]);
var_dump($profile_got[4]);
and var_dump($profile_got); returns NULL.
I've tried many different codes, but they didn't work also.
I hope you can help me with not doing all requests separately.
$profile_get = json_decode(file_get_contents('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key='.$mywebapikey.'&steamids='.$stmid64['capt'].','.$stmid64['p2'].','.$stmid64['p3'].','.$stmid64['p4'].','.$stmid64['p5']),true);
for ($i = 0; $i < 5; $i++) {
if ($i == 0)
$player = 'capt';
else
$player = 'p'.($i+1);
for ($j = 0; $j < 5; $j++) {
if ($stmid64[$player] == $profile_get['response']['players'][$j]['steamid']) {
$profile_got[$i] = $profile_get['response']['players'][$j];
break;
}
}
}
var_dump($profile_got[0]);
var_dump($profile_got[1]);
var_dump($profile_got[2]);
var_dump($profile_got[3]);
var_dump($profile_got[4]);
cheers
this will work as intended, it will order your array by 'capt' (why not p1 instead ? , you could save 3 lines), 'p2', 'p3', 'p4', 'p5'. you can still extend this in many ways but basically this is how to put them in the right order. also mind that i stored your (well mine) api key inside a $var

php sql find and insert in empty slot

I have a game script thing set up, and when it creates a new character I want it to find an empty address for that players house.
The two relevant table fields it inserts are 'city' and 'number'. The 'city' is a random number out of 10, and the 'number' can be 1-250.
What it needs to do though is make sure there's not already an entry with the 2 random numbers it finds in the 'HOUSES' table, and if there is, then change the numbers. Repeat until it finds an 'address' not in use, then insert it.
I have a method set up to do this, but I know it's shoddy- there's probably some more logical and easier way. Any ideas?
UPDATE
Here's my current code:
$found = 0;
while ($found == 0) {
$num = (rand()%250)+1; $city = (rand()%10)+1;
$sql_result2 = mysql_query("SELECT * FROM houses WHERE city='$city' AND number='$num'", $db);
if (mysql_num_rows($sql_result2) == 0) { $found = 1; }
}
You can either do this in PHP as you do or by using a MySQL trigger.
If you stick to the PHP way, then instead of generating a number every time, do something like this
$found = 0;
$cityarr = array();
$numberarr = array();
//create the cityarr
for($i=1; $i<=10;$i++)
$cityarr[] = i;
//create the numberarr
for($i=1; $i<=250;$i++)
$numberarr[] = i;
//shuffle the arrays
shuffle($cityarr);
shuffle($numberarr);
//iterate until you find n unused one
foreach($cityarr as $city) {
foreach($numberarr as $num) {
$sql_result2 = mysql_query("SELECT * FROM houses
WHERE city='$city' AND number='$num'", $db);
if (mysql_num_rows($sql_result2) == 0) {
$found = 1;
break;
}
}
if($found) break;
}
this way you don't check the same value more than once, and you still check randomly.
But you should really consider fetching all your records before the loops, so you only have one query. That would also increase the performance a lot.
like
$taken = array();
for($i=1; $i<=10;$i++)
$taken[i] = array();
$records = mysql_query("SELECT * FROM houses", $db);
while($rec = mysql_fetch_assoc($records)) {
$taken[$rec['city']][] = $rec['number'];
}
for($i=1; $i<=10;$i++)
$cityarr[] = i;
for($i=1; $i<=250;$i++)
$numberarr[] = i;
foreach($cityarr as $city) {
foreach($numberarr as $num) {
if(in_array($num, $taken[]) {
$cityNotTaken = $city;
$numberNotTaken = $number;
$found = 1;
break;
}
}
if($found) break;
}
echo 'City ' . $cityNotTaken . ' number ' . $numberNotTaken . ' is not taken!';
I would go with this method :-)
Doing it the way you say can cause problems when there is only a couple (or even 1 left). It could take ages for the script to find an empty house.
What I recommend doing is insert all 2500 records in the database (combo 1-10 with 1-250) and mark with it if it's empty or not (or create a combo table with user <> house) and match it on that.
With MySQL you can select a random entry from the database witch is empty within no-time!
Because it's only 2500 records, you can do ORDER BY RAND() LIMIT 1 to get a random row. I don't recommend this when you have much more records.

Limit number of records php - flat file

I am using flat file db, not mysql so I can't use $limit
I need to limit the number of records to 1, if more than 1 then echos something else:
$result = $db->getall(lmonth);
foreach($result as $item)
show_record($item);
}
Function getall()
/*!
* #function getall
* #abstract retrieves all records in the database, each record in an array
* element.
* #param orderby order the results. Set to the field name to order by
* (as a string). If left unset, sorting is not done and it is a lot faster.
* If prefixed by "!", results will be ordered in reverse order.
* If orderby is an array, the 1st element refers to the field to order by,
* and the 2nd, a function that will take two take two parameters A and B
* - two fields from two records - used to do the ordering. It is expected
* that the function would return -ve if A < B and +ve if A > B, or zero
* if A == B (to order in ascending order).
* #param includeindex if true, an extra field called 'FFDB_IFIELD' will
* be added to each record returned. It will contain an int that specifies
* the original position in the database (zero based) that the record is
* positioned. It might be useful when an orderby is used, and an future
* operation on a record is required, given it's index in the table.
* #result all database records as an array
*/
function getall($orderby = NULL, $includeindex = false)
{
if (!$this->isopen)
{
user_error("Database not open.", E_USER_ERROR);
return false;
}
// If there are no records, return
if ($this->records == 0)
return array();
if (!$this->lock_read())
return false;
// Read the index
$index = $this->read_index();
// Read each record and add it to an array
$rcount = 0;
foreach($index as $offset)
{
// Read the record
list($record, $rsize) = $this->read_record($this->data_fp, $offset);
// Add the index field if required
if ($includeindex)
$record[FFDB_IFIELD] = $rcount++;
// Add it to the result
$result[] = $record;
}
$this->unlock();
// Re-order as required
if ($orderby !== NULL)
return $this->order_by($result, $orderby);
else
return $result;
}
Function show_record()
function show_record($record){
$month = $record["lmonth"];
$status = $record["lstatus"];
$year = $record["lyear"];
}
if (($status == ON) && ($month >= $current_month) && ($year >= $current_year)){
echo "foo";
}
I tried to use break but it comes back 0(zero) records.
I tried using $i = 0...but it returned all or nothing
Any ideas?
Thanks
What about something like this?
function getall($orderby = null, $includeindex = false, $limit = null) {
...
if ($orderby !== null) {
$result = $this->order_by($result, $orderby);
}
if ($limit) {
return array_slice($result, 0, $limit);
}
return $result;
}
SOLUTION
Print_r did the trick, and I was using echo:
print_r (show_record($row));
Here is how the final code is working for me:
$result = $db->getall(lp_month,lp_year);
$i = 0;
foreach ($result as $row){
print_r (show_record($row));
if ($i >= 1)
break;
$i++;
}
Now looking forward to solve other minor problems, thanks

Explode Flat File Result

I am using flat file db, not mysql so I can't use $limit
Function getbyfieldsw()
/*!
* #function getbyfieldsw
* #abstract retrieves records in the database whose field matches the
* given regular expression.
* #param fieldname the field which to do matching on
* #param regex the regular expression to match a field on.
* Note: you should include the delimiters ("/php/i" for example).
* #param orderby order the results. Set to the field name to order by
* (as a string). If left unset, sorting is not done and it is a lot faster.
* If prefixed by "!", results will be ordered in reverse order.
* If orderby is an array, the 1st element refers to the field to order by,
* and the 2nd, a function that will take two take two parameters A and B
* - two fields from two records - used to do the ordering. It is expected
* that the function would return -ve if A < B and +ve if A > B, or zero
* if A == B (to order in ascending order).
* #param includeindex if true, an extra field called 'FFDB_IFIELD' will
* be added to each record returned. It will contain an int that specifies
* the original position in the database (zero based) that the record is
* positioned. It might be useful when an orderby is used, and an future
* operation on a record is required, given it's index in the table.
* #result matching records in an array, or false on failure
*/
function getbyfieldsw($fieldname, $orderby = NULL, $includeindex = false) {
if (!$this->isopen)
{
user_error("Database not open.", E_USER_ERROR);
return false;
}
// Check the field name
if (!$this->key_exists_array($fieldname, $this->fields))
{
user_error(
"Invalid field name for getbyfield: $fieldname",
E_USER_ERROR
);
return false;
}
// If there are no records, return
if ($this->records == 0)
return array();
if (!$this->lock_read())
return false;
// Read the index
$index = $this->read_index();
// Read each record and add it to an array
$rcount = 0;
foreach($index as $offset)
{
// Read the record
list($record, $rsize) = $this->read_record($this->data_fp, $offset);
// See if the record matches the regular expression
// Add the index field if required
if ($includeindex)
$record[FFDB_INDEX_RECORDS_OFFSET] = $rcount;
$result[] = $record;
++$rcount;
}
$this->unlock();
// Re-order as required
if ($orderby !== NULL)
return $this->order_by($result, $orderby);
else
return $result;
}
Function show_record()
function show_record($record){
$month = $record["lmonth"];
$status = $record["lstatus"];
$year = $record["lyear"];
}
if (($status == ON) && ($month >= $current_month) && ($year >= $current_year)){
echo "foo";
}
Call records - here is the problem - this works except for the break; - I need to explode the flat file to read each record individually, then add to the foreach():
$result = $db->getbyfieldsw(lp_month);
$i = 0;
foreach ($result as $item){
show_record($item);
if ($i >= 2)
break;
}
Since this is flat file, when calling the function getbyfieldsw() the file comes flat as a single file, no matter how many records on it, records = 1 or records = 100 is the same at this point.
Break; does not work because all records come as single record - therefore nothing to break.
What I want to do is split/explode the records, count them, and based on my if statement post:
if $i == 0 echo "content1";
if $i == 1 echo "content2";
if $i > 1 echo "content 3";
I got held here for the past 3 days...exhausted all solutions.
HELP glad appreciated
Thanks
Sorry for not helping your code directly, but you should consider switching to SQLITE instead of using your own database system. SQLITE is flat, open-source, efficient and easier to work with. PHP's sqlite module has everything you need. You should check it out!
http://www.sqlite.org/about.html
http://php.net/manual/en/book.sqlite.php

php - calculate price multiplied by quantity for several items to get individual totals and a grand total

I have a form that has 7 different items. The user will be able to input quantity (unlimited) and the price will be set for each item (let's say for example, item 1 is $18, and item 2 is $20, etc.). What I have done with another form that's almost identical is this:
if(!empty($_POST[qty_item_1])) {
$total_1 = ($_POST[qty_item_1] * 18);
} else {
$total_1 = "0";
}
if(!empty($_POST[qty_item_2])) {
$total_2 = ($_POST[qty_item_2] * 20);
} else {
$total_2 = "0";
}
I would have a code block like those for each item. It seems to work fine but I feel like this is probably the hard way to do it but I'm having trouble figuring out what else I might do. Any suggestions?
Given your field names, you could do something like:
$totals = array();
for ($i = 1; $i <= 7; $i++) {
$total[$i] = isset($_POST["qty_item_{$i}"]) ? intval($_POST["qty_item_{$i}"]) : 0;
}
The other option is to simply name your fields qty_item[]. When PHP parses the submitted data, it'll convert all those qty_item fields into an array for you. You'd still need to post-process to make sure that they contain valid numbers and whatnot, thoguh.

Categories