Sorry I am new to Laravel and PHP. I have a web application code that I need to debug. I run the code, and it gives me ErrorException Trying to get property 'name' of non-object. I looked it up, and I know it's a common problem but I still cannot figure out what is causing this error. I already looked at this page and I don't understand: Reference - What does this error mean in PHP?
This is my controller code:
function getActivity(){
$lead_history = Lead::with('user','eventTypeTrashed','locationTrashed')->get();
$event_history = Event::with('user','booking','contactus.event_type_trashed','booking.location_trashed')->get();
$data = [];
foreach ($lead_history as $key => $leads){
if(count($leads->revisionHistory) > 0){
foreach ($leads->revisionHistory as $history){
$date_diff = \DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s'))->diff(\DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s',strtotime($history->updated_at))));
if($date_diff->d > 0 ){
$date = $date_diff->d . ' days ago';
} elseif($date_diff->h > 0){
$date = $date_diff->h . ' hours ago';
}else{
$date = $date_diff->i . ' minutes ago';
}
$data[] = [
'id' => $leads->id,
'type' => 'lead',
'image' => $history->userResponsible()->user_avatar,
'user' => $history->userResponsible()->first_name .' '. $history->userResponsible()->last_name,
'user_id' => $history->userResponsible()->id,
'key' => ucwords(str_replace("_"," ",$history->fieldName())),
'client' => $leads->client_name,
'status' => 'update',
'old_value' =>$history->oldValue(),
'new_value' =>$history->newValue(),
'updated_at' => $history->updated_at,
'time_diff' =>$date,
'priority' => $leads->priority,
'location' => $leads->locationTrashed->name, // This line casuses an error
'event_type' => ($leads->eventTypeTrashed) ? $leads->eventTypeTrashed->name : ''
];
}
}
$date_diff = \DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s'))->diff(\DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s',strtotime($leads->created_at))));
if($date_diff->d > 0){
$date = $date_diff->d . ' days ago';
} elseif($date_diff->h > 0){
$date = $date_diff->h . ' hours ago';
}else{
$date = $date_diff->i . ' minutes ago';
}
$data[] = [
'id' => $leads->id,
'type' => 'lead',
'image' => $leads->user->user_avatar,
'user' => $leads->user->first_name .' '. $leads->user->last_name,
'user_id' => $leads->user->id,
'key' => '',
'client' => $leads->client_name,
'status' => 'created',
'updated_at' => $leads->created_at,
'old_value' =>'',
'new_value' =>'',
'time_diff' =>$date,
'priority' => $leads->priority,
'location' => $leads->locationTrashed->name,
'event_type' => ($leads->eventTypeTrashed) ? $leads->eventTypeTrashed->name : ''
];
}
foreach ($event_history as $key => $events){
if(count($events->revisionHistory) > 0){
foreach ($events->revisionHistory as $history){
$date_diff = \DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s'))->diff(\DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s',strtotime($history->updated_at))));
if($date_diff->d > 0){
$date = $date_diff->d . ' days ago';
}elseif ($date_diff->h > 0){
$date = $date_diff->h . ' hours ago';
}else{
$date = $date_diff->i . ' minutes ago';
}
$data[] = [
'id' => $events->id,
'type' => 'event',
'image' => $history->userResponsible()->user_avatar,
'user' => $history->userResponsible()->first_name .' '. $history->userResponsible()->last_name,
'user_id' => $history->userResponsible()->id,
'key' => ucwords(str_replace("_"," ",$history->fieldName())),
'client' => $events->booking->booking_name,
'status' => 'update',
'updated_at' => $history->updated_at,
'old_value' =>$history->oldValue(),
'new_value' =>$history->newValue(),
'time_diff' =>$date,
'priority' => $events->status,
'location' => $events->booking->location_trashed->name,
'event_type' => $events->contactus->event_type_trashed->name
];
}
}
$date_diff = \DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s'))->diff(\DateTime::createFromFormat('Y-m-d H:i:s',date('Y-m-d H:i:s',strtotime($events->created_at))));
if($date_diff->d > 0){
$date = $date_diff->d . ' days ago';
}elseif($date_diff->h > 0){
$date = $date_diff->h . ' hours ago';
}else{
$date = $date_diff->i . ' minutes ago';
}
$data[] = [
'id' => $events->id,
'type' => 'event',
'image' => ($events->user) ? $events->user->user_avatar : '',
'user' => ($events->user) ? $events->user->first_name .' '. $events->user->last_name : '',
'user_id' => ($events->user) ? $events->user->id : '',
'key' => '',
'client' => $events->booking->booking_name,
'status' => 'created',
'updated_at' => $events->created_at,
'old_value' =>'',
'new_value' =>'',
'time_diff' => $date,
'priority' => $events->status,
'location' => $events->booking->location_trashed->name,
'event_type' => $events->contactus->event_type_trashed->name
];
}
usort($data, function ($a, $b){
$dateA = \DateTime::createFromFormat('Y-m-d H:i:s', $a['updated_at']);
$dateB = \DateTime::createFromFormat('Y-m-d H:i:s', $b['updated_at']);
return $dateB >= $dateA;
});
return $data;
}
Sorry if this is a bad question and please tell me if I need to include anything else
This error occurs when there's no relational record found. Like in your code you've a relation "locationTrashed". When lead don't have any record related to it in location_trashed table it'll return error. In Laravel 8 you can return default model result if not results available against record or just simply use ($param ?? null).
You have a lot going on there! I have some suggestions that might make your life easier and your code cleaner. But first, the missing 'name' error...
'location' => $leads->locationTrashed->name,
Relationship Name
First thing I noticed about this relationship is the name should be 'location_trashed' instead of the camelCase 'locationTrashed'. In your other models, it is 'location_trashed', so make sure this is correct. I'll continue to reference it as locationTrashed, since this is what your current code reflects.
Finding the Cause
You're probably correct in identifying this line as causing the error. The issue is probably that the locationTrashed doesn't exist. You should look for the locationTrashed_id (or location_trashed_id) to figure out what the id is, and make sure the item exists in the database and hasn't been deleted.
Which Object Doesn't Exist?
If you aren't sure which object is causing the error, you can try dumping the object if the relationship doesn't exist. Laravel has convenient methods like has() and doesntHave() to make this easy. Put this right above the '$data[] = [' line.
https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
if($leads->doesntHave('locationTrashed')) {
dd($leads);
}
$data[] = [
'id' => $leads->id,
'type' => 'lead',
...
This will let you see the object in question and determine which related 'locationTrashed' is missing.
Or... ignore it
If you need the relationship, use the above to try to figure it out. However, if the relationship isn't there because it doesn't always exist, set it to be ignored.
You can use an exists() method on the relationship to check if it exists.
'location' => ($leads->locationTrashed()->exists()) ? $leads->locationTrashed->name : ''
Now some suggestions:
Carbon for Dates
You should really look into using Carbon. This will take the 8 lines of code you have and reduce it to:
$date_diff = Carbon::parse($history->updated_at)->diffForHumans();
https://carbon.nesbot.com/docs/#api-humandiff
Attribute Accessors
Your user model has a first_name and last_name attribute that you're putting together to display their full name. If you do this often, you can set up an attribute accessor so you don't have keep doing it.
// User Model
public function getFullNameAttribute() {
return $this->first_name.' '.$this->last_name;
}
// Controller
'user' => $history->userResponsible->full_name
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
Laravel Collections
When working with arrays, you can instead convert them into collections and take advantage of the many useful methods that it provides. Instead of ordering the data items yourself, you can use the sortBy() method:
$collection = collect($data)->sortBy('updated_at');
$collection = collect($data)->sortByDesc('updated_at'); // or descending
https://laravel.com/docs/8.x/collections
Related
I am trying to optimize my query for a page with pagination results in Cakephp and therefore I am looking to calculate the exact time with the PHP function microtime().
That way, I will know better how much time it's taking to execute the certain request.
Moreover, I am trying to cache the results of the pagination through Cache::read and Cache::write, which both are internal Cakephp functions as regards caching methods in Cakephp 2.x.
Thus, what I have at this point is this:
I am thinking that in order to calculate exactly the whole timelapse, should I put the rest of the code inside the while loop ?
Any help would be quite appreciating.
Thanks
$start = microtime(true);
while ($start) {
$this->paginate = $options;
$resultado = $this->paginate('Doctor');
}
$time_elapsed_secs = microtime(true) - $start;
pr($time_elapsed_secs);
foreach ($resultado AS $k => $r) {
$hasProduct = Cache::read(__('thedoctors::') . 'hasProduct_by_dr_' . PAIS . '_' . $r['Doctor']['id'], '1day');
if (empty($hasProduct)) {
$hasProduct = $this->DoctorsProduct->find('all', [
'recursive' => -1,
'fields' => ['Product.*', 'DoctorsProduct.*'],
'joins' => [
['table' => 'td_products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => [
'Product.id = DoctorsProduct.id_product'
]
]
],
'conditions' => [
'id_doctor' => $r['Doctor']['id'],
'DoctorsProduct.status' => 1
],
'order' => [
'Product.id ASC',
]
]);
Cache::write(__('thedoctors::') . 'hasProduct_by_dr_' . PAIS, $hasProduct, '1day');
}
$resultado[$k]['Products'] = $hasProduct;
$resultado[$k]['Article'] = 0;
}
Well, the approach which I posted it is indeed wrong, that would possibly just loop infinitely. Finally, what I found as a solution to the question would be to just put the microtime function at the very beginning. I declare a new variable as $time.
After this, I am just substracting the actual microtime with the time which was declared beforewards. Works like a charm.
$time = microtime( TRUE );
foreach ($resultado AS $k => $r) {
$hasProduct = Cache::read(__('thedoctors::') . 'hasProduct_by_dr_' . PAIS . '_' . $r['Doctor']['id'], '1day');
if (empty($hasProduct)) {
$hasProduct = $this->DoctorsProduct->find('all', [
'fields' => ['Product.*', 'DoctorsProduct.*'],
'joins' => [
['table' => 'td_products',
'alias' => 'Product',
'type' => 'INNER',
'conditions' => [
'Product.id = DoctorsProduct.id_product'
]
],
],
'conditions' => [
'id_doctor' => $r['Doctor']['id'],
'DoctorsProduct.status' => 1
],
'order' => [
'Product.id ASC',
]
]);
Cache::write(__('thedoctors::') . 'hasProduct_by_dr_' . PAIS,
$hasProduct, '1day');
}
$resultado[$k]['Products'] = $hasProduct;
$resultado[$k]['Article'] = 0;
}
$time = microtime( TRUE ) - $time;
echo $time;
die("123");
I have a working query but its only returning 1 row - where or what can I do to return all existing rows?
Model:
public function getInfo() {
$info_query = $this->db->query("SELECT * FROM `km_info` WHERE ((date_start = '0000-00-00' OR date_start < NOW()) AND (date_end = '0000-00-00' OR date_end > NOW())) AND status = '1'");
if ($info_query->num_rows){
return array(
'info_id' => $info_query->row['info_id'],
'name' => $info_query->row['name'],
'amount' => $info_query->row['amount'],
'date_start' => $info_query->row['date_start'],
'date_end' => $info_query->row['date_end'],
'status' => $info_query->row['status'],
'date_added' => $info_query->row['date_added']
);
}
}
Controller:
$info_options = $this->model_extension_total_info->getInfo();
if ($info_options){
$json['info_options'] = array();
$json['info_options'][] = array(
'info_id' => $info_options['info_id'],
'name' => $info_options['name'],
'amount' => $info_options['amount'],
'date_start' => $info_options['date_start'],
'date_end' => $info_options['date_end'],
'status' => $info_options['status'],
'date_added' => $info_options['date_added']
);
}
When I try foreach() in the controller, I still only get one row.:
foreach ($info_options as $info) {
var_dump($info_options['name']);exit;
//var_dump($info_options['name']); results: Warning: Illegal string offset 'name'
}
In the model, when I dump:
$info_query I get 9 -- which is all of the rows I'm expecting.
Not particularly certain what I'm missing or doing wrong.
You're not looping over the result. Not in model nor in the controller. Though I don't know why you'd do this twice. Maybe I'm just not understanding your code.
Model:
$data = [];
if ($info_query->num_rows){
foreach ($info_query->result() as $row) {
$data[] = array(
'info_id' => $row['info_id'],
'name' => $row['name'],
'amount' => $row['amount'],
'date_start' => $row['date_start'],
'date_end' => $row['date_end'],
'status' => $row['status'],
'date_added' => $row['date_added']
);
}
return $data;
}
Controller:
$info_options = $this->model_extension_total_info->getInfo();
$json['info_options'] = array();
if ($info_options){
foreach ($info_options as $info) {
$json['info_options'][] = array(
'info_id' => $info['info_id'],
'name' => $info['name'],
'amount' => $info['amount'],
'date_start' => $info['date_start'],
'date_end' => $info['date_end'],
'status' => $info['status'],
'date_added' => $info['date_added']
);
}
}
Technically, you don't have to loop at all. You can just:
public function getInfo() {
$info_query = $this->db->query("SELECT * FROM `km_info` WHERE ((date_start = '0000-00-00' OR date_start < NOW()) AND (date_end = '0000-00-00' OR date_end > NOW())) AND status = '1'");
if ($info_query->num_rows){
return $info_query->result();
}
else
{
return false;
}
}
and
$info_options = $this->model_extension_total_info->getInfo();
// and that's it, just do whatever you need with $info_options
I am trying to sort a multidimensional array based on date/time, however it doesn't seem to be working correctly when I do a print_r. My best guess is that the time I provided to strtotime() is not in the correct format however the date and time formats are both listed, but separately in the php manual and no errors are thrown.
The format I use is unclear in the code so here it is: yyyy-mm-dd hhmm (24h with no colon GMT)
Here is the code:
function dateSort($a, $b){
$d1 = strtotime($a['date'].' '.$a['startTime']);
$d2 = strtotime($b['date'].' '.$a['startTime']);
return $d1 - $d2;
}
usort($events, 'dateSort');
print_r($events);
IVAO.
You had a typo in third line of snippet. Second line refers to $a, but in 3rd you mix both $b and $a :).
Also, I think, you needn't use strtotime at all. Look into snippet:
<?php
function dateSort($a, $b)
{
$d1 = floatval(str_replace('-', '', $a['date']) . " $a[startTime]");
$d2 = floatval(str_replace('-', '', $b['date']) . " $b[startTime]");
return $d1 - $d2;
}
$events = [
['date' => '2015-05-01', 'startTime' => '2300', 'value' => 'Event 1'],
['date' => '2012-05-01', 'startTime' => '1430', 'value' => 'Event 2'],
['date' => '2011-09-17', 'startTime' => '1021', 'value' => 'Event 3'],
['date' => '2001-01-22', 'startTime' => '0959', 'value' => 'Event 4'],
['date' => '1999-02-05', 'startTime' => '1740', 'value' => 'Event 5'],
];
usort($events, 'dateSort');
echo '<pre>' . print_r($events, 1) . '</pre>';
And click to codepad.
From Php manual, you can try to update your dateSort() function
function dateSort($a, $b){
$d1 = strtotime($a['date'].' '.$a['startTime']);
$d2 = strtotime($b['date'].' '.$a['startTime']);
return ($d1 < $d2) ? -1 : 1;
}
Suggest you to give us some of your output, easier to take it from there.
I have an array that contain user's activities on the website. It contains activities such as writing comments, news and groups. If two comments (or more) from different users have been written within an hour, I would like to gather those two arrays into one: User and 2 more commented on X. The code I have so far looks like this:
<?php
$output = array();
$output[] = array('userID' => 12, 'txt' => sprintf('%s commented in %s', 'User1', 'Event'), 'date' => 1393080072);
$output[] = array('userID' => 13, 'txt' => sprintf('%s commented in %s', 'User2', 'Event'), 'date' => 1393080076);
$output[] = array('userID' => 13, 'txt' => sprintf('%s created the news %s', 'User2', 'RANDOMNEWS'), 'date' => 1393080080);
$output[] = array('userID' => 14, 'txt' => sprintf('%s commented in %s', 'User3', 'Event'), 'date' => 1393080088);
$date = array();
foreach($output as $k => $d) {
$date[$k] = $d['date'];
}
array_multisort($date, SORT_DESC, $output);
print_r($output);
?>
So far the code above sorts the arrays by date (DESC). Desired result: one array: %s and 2 more commented in... and the other arrays removed from output. So by taking the latest comment and checking the date from the rest of the comments, it should be possible to handle this. I simply need some suggestions.
Thanks in advance
From what I understand from your question, I think you want to find out the number of users commenting in the last hour with respect to the latest commentor.
Using your logic, array_filter can help get those values which lie in the last hour.
This is the continuation of your code -
/*
...your code...
*/
$latest_time = $output[0]['date'];
$hour_past_time = $latest_time - 3600;
$user_ids = Array();
$res=array_values(
array_filter($output,function($arr)use($latest_time, $hour_past_time,&$user_ids){
if(
$arr["date"] <= $latest_time &&
$arr["date"] >= $hour_past_time &&
in_array($arr['userID'],$user_ids) == false
){
$user_ids[] = $arr['userID'];
return true;
}
}
)
);
echo "Users with their latest comments in the past hour- <br />";
var_dump($res);
$latest_user_id = "User".$res[0]['userID'];
$rest = count($res) - 1;
echo "<br />$latest_user_id and $rest more commented.<br />";
OUTPUT -
Users with their latest comments in the past hour-
array
0 =>
array
'userID' => int 14
'txt' => string 'User3 commented in Event' (length=24)
'date' => int 1393080088
1 =>
array
'userID' => int 13
'txt' => string 'User2 created the news RANDOMNEWS' (length=33)
'date' => int 1393080080
2 =>
array
'userID' => int 12
'txt' => string 'User1 commented in Event' (length=24)
'date' => int 1393080072
User14 and 2 more commented.
Hope this helps-
first of, just wanted to let you know that I am a newbie at CI. but I am having trouble with this piece of code where is breaking and I can't seem to be able to find the answer anywhere.
for some reason the code is breaking at the first if statement.. if possible could you help me out understand what is really happening there?
Thank you all for your help!
function main
{
$this->load->model(getData) psudo code for this area...
}
---model---
function getData....
{
Sql = this->db->query(sql code that returns all the information required.)
$result = $sql->result_array();
$types = array ( 'EVENT|TIME' => array( 'id' => 1, 'name' => 'Regular' ),
'PROPOSITION|REGULAR' => array( 'id' => 2, 'name' =>'Propositions'),
'EVENT|TIME' => array( 'id' => 3, 'name' => 'Now' ),
'PROPOSITION|FUTURES' => array( 'id' => 4, 'name' => 'Future' ));
$var = array();
foreach ($result as $event) {
$cat = $event['type'] . '|' . $event['sub_type'];
$typeId = $types[$cat]['id'];
if(!is_array($var[$event['event_id']]['var'][$event['type_id']]))
{
if(!is_array($var[$event['event_id']]))
{
$var[$event['event_id']] = array( 'event_name' =>
$event['event_name'],'event_abbreviation' =>
$event['event_abbreviation']);
}
$var[$event['event_id']]['var'][$event['type_id']] = array(
'type_name' => $event['abbreviation'],'type_abbreviation' => $event['name']
);
}
$event[$event['event_id']]['var'][$event['type_id']]['types'][$typeId] =
$types[$cat]['name'];
}
return $myResults;
}
In this line
if(!is_array($var[$event['event_id']]['var']$event['type_id']]))
You are missing a [ somewhere. I'm guessing before $event['type_id'].
So replace with:
if(!is_array($var[$event['event_id']]['var'][$event['type_id']]))