add row to the DB:: result in Laravel - php

I have a table BirthDeath like below:
id birthmonth deathmonth
1 1 3
2 4 4
3 2 5
The query is for getting either birth or death month,
$events = DB::table('BirthDeath')->where('birthmonth',4)->orWhere('deathmonth',4)->paginate(10);
This query results in 1 row, however, I need this row to be duplicated (because the birth and death month are the same) and in the view, I am intending to show them separately. My current solution is to duplicate the results if the birth and death month are the same but it does not work. Here is the code
events2 = $events;
foreach ($events as $event) {
if ($event->birthmonth == $CurrentMonth || $event->deathmonth == $CurrentMonth) {
$events2->appends($event); <-------- this is the cause of the error
}
}

If you want to add items to a Collection you can use the add or push method. add takes a single item and push can take many (variable amount of arguments):
$events->add($event);
// or
$events->push($event);

Related

How to check if a value is greater than within an array

I have the following SQL statement:
$query = "SELECT item, COUNT(*) as number FROM shop GROUP BY item";
This will give me the following result:
item number
item1 23
item2 15
item3 4
I want to use this to make menu items, so normally the menu would look:
item1
item2
item3
But I want to do a check if an item has less than 10 records, that I don't want to display this item.
So in this example, the menu would be like:
item1
item2
Any idea how to achieve this?
I would like to do this in PHP because I need all the items in the query but will only want to show them which are greater then 10 and need the other items later on.
If you want to do this in PHP then you can do like this
function filterArray($value){
return ($value.number > 10);
}
$filteredArray = array_filter($yourDBArray, 'filterArray');
foreach($filteredArray as $k => $v){
//your desired array
}
In terms of speed Mysql option is good as suggested above.
Just change your query from
SELECT item, COUNT(*) as number FROM shop GROUP BY item
to
SELECT item, COUNT(*) as number FROM shop GROUP BY item HAVING number>=10
As you really need to perform this in PHP you could use array_filter() which, using a closure, will remove items which number is less than 10:
$more_than_ten = array_filter($items, function ($i) { return $i['number'] >= 10; });
Doing it with SQL would be a better solution (about performances). In case you'd need it, you could use the HAVING clause (you can't perform a WHERE number >= 10):
SELECT
item,
COUNT(*) as number
FROM shop
GROUP BY item
HAVING number >= 10
I noticed php is tagged. For the sake of options, here's how I'd go about separating the unneeded data in php if you were to get it from the database as-is:
foreach ($data as $item) {
$num = (int) $item['number']; // force of habit
if ($num >= 10) {
// display it
}
}
I'd probably separate the data at the database step, but this works if it's the route you want to take.
There is two options to filter the data so only the rows with more then 10 will appear.
At the SQL query
__
SELECT item, COUNT(*) as number FROM shop GROUP BY item HAVING number > 9
This will cause you to recieve only the requested rows from the database
Filter with PHP - every time you want to print the menu or testing it out, just can the value of 'number' in the array reutrned from the query. You can also allocate new array and insert all the values that contains 'number' that bigger then 10.

how to retrieve due fee month wise student?

I'm try to fetch due month which, students not submitted there fee in his profile its show like your fee is due in this month,example- if a student not paid his/her fee in february and january then its his account that you fee is due in february and january please submit fee.I'm able to fetch all students who not submitted fee but how to show month(<=current month) all previous month fee dues
here is my code for profile
public function show($reg_no = null)
{
$s = Student::where('reg_no', $reg_no)->with('courses','states','sections','city','ccity','sstates')->first();
return view('students.student',compact('s'));
}
for students who not submitted fee in current month all students
$dt = Carbon::now();
$query = Student::where('status',1)->whereDoesntHave('subscriptions', function ($queryt) use($dt) {
$queryt->whereMonth('created_at','=',$dt->month);
})->with('courses')->paginate(15);
and here is my subscriptions table
I'll show you an example, but don't just copy paste, because this code is only to help you to understand how to achieve it. You'll need to make some change to fit in your app.
$messages = array();
foreach ($Student->subcriptions as subcription) {
if ($subcription->late_fee != null) {
$month = $subcription->month->month(); // I'm not sure if Carbon will handle this part. If not, you'll need to do an explode of $subcription->month then take the $month[1]...
if ($month == 1) {$month = 'January';}
if ($month == 2) {$month = 'February';}
... //Do it for every month...
$message = 'Your fee are due for '.$month; //Or you can only push the month then into the view then implode the array.
array_push($messages, $message);
}
}
Into your view, if you are using blade, you simply do this where you want to show the messages. Don't forget to pass $messages to the view.
#foreach($messages as $message)
<span>{{$message}}</span>
#endforeach
There is multiple way to do what you want. But this will show every month where the student have late_fee not null. Now adapt it to your code.
Don't forget that if you don't want something like:
Your fee are due for January.
Your fee are due for February.
Your fee are due for Mars.
....
Look for implode of the messages array without adding Your fee are due for into the loop then don't use the for each loop into the view, simply display the new imploded variable.

separate array object according to date

I have a array of events with unixtimestamp and i want to show them according to year. Mean section wise.
2015
Event 1
Event 2
Event 3
2014
Event 1
Event 2
Event 3
What i do:
$yearlyEvents=array();
foreach ($events as $event) {
$eventPost = get_post($event->post_id);
$timestamp=$event->start;
$eventYear=gmdate("Y", $timestamp);
if($index=in_array($eventYear, $yearlyEvents, true)){
print_r($index);
}
else{
$tempObj['name']=$eventYear;
$tempObj['events']=$event;
$yearlyEvents[]=$tempObj;
}
}
But not get the desired results.Anybody help?
You need to get events as sub arrays of year.
Add sub arrays, one for each year append events to it.
This is my preferred logic, please feel free to change it in accordance with your project needs.
Corrected Code:
$yearlyEvents=array();
foreach ($events as $event) {
$eventPost = get_post($event->post_id);
$timestamp=$event->start;
$eventYear=gmdate("Y", $timestamp);
if($index=in_array($eventYear, $yearlyEvents, true)){
print_r($index);
}
else{
$tempObj['name']=$eventYear;
$tempObj['events']=$event;
$yearlyEvents[$eventYear][] = $tempObj; // Check here.
}
}
first of all you need to get result from Database in such a format which is easy to manipulate for your output.
Here you are trying to display events years wise in descending order and events in ascending order. So first of all get result from database like
SELECT * FROM events ORDER by events.start DESC,events.post_id ASC
Now it is in an order which is easy to display. loop through and display result until next year found. check if next year come display year too.

PHP: Rotating through an array

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.

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.

Categories