Laravel cannot echo softdeletes field error Carbon Data Missing - php

I wanna check if the data has been deletes. So i use eloquent like:
$check = ModelName::withTrashed()->where("id","=",$id)
->select("id","deleted_at","other_field")
->get();
foreach ($check as $rw) {
echo $rw->id; // it print the id
echo $rw->other_field; //also print the field value
echo $rw->deleted_at; // Error:
//InvalidArgumentException in Carbon.php line 425:
//Data missing
//at Carbon::createFromFormat('Y-m-d H:i:s.000', '2016-09-19 07:06:56') in Model.php line 2955
}
Then i try to dump to see the value:
dd($check);
The result of deleted_at echo it value like:
#attributes: array:3 [▼
"id" => 1
"deleted_at" => "2016-09-19 07:06:56"

EDIT:
In the error message, date format used by Carbon seems to be a bit off. Try to specify it in your model manually:
protected $dateFormat = 'Y-m-d H:i:s';
I'm leaving the first part of the answer because that might also help someone who stumbles on this topic through search.
dd() stops after the first value, which is soft-deleted in your case, so you simply stop execution before the error. If you remove dd, the loop continues until it breaks. So if you wanna check the all the data up until the error, use print_r instead of dd(), so that output would not stop the script.
Now about your exception. The problem is when Carbon encouters a field that is NULL - a record that's not deleted. It tries to parse null into a date and obviously fails.
Your approach should be changed - you could check if the record is soft deleted before trying to access it's deleted_at timestamp:
$check = ModelName::withTrashed()->where("id","=",$id)
->select("id","deleted_at","other_field")
->get();
foreach ($check as $rw) {
echo $rw->id; // it print the id
echo $rw->other_field; //also print the field value
if ($rw->trashed())
echo $rw->deleted_at;
}
Another, less elegant solution would be to remove deleted_at from $dates array in model and stop the automatic parsing. Then you would write a custom accessor in your model:
public function getDeletedAtAttribute($value)
{
return $value ? Carbon::createFromFormat('Y-m-d H:i:s', $value) : NULL;
}
I would recommend the first approach.

Related

Using PHP Faker in Laravel to generate "unique with" entry when seeding a database using a factory

So Similar to the unique with validation rule (See: https://github.com/felixkiss/uniquewith-validator), I want to know how to generate a entry, where one column is unique with another one. I want to seed my database as follows.
Example:
There are 12 steps in the "steps" table. Each step should have 5 categories associated with each one that are stored in the "step_categories" table. Each of those categories are assigned a unique order number 1 through 5 that is unique with each "step_id".
See this image here for an example of what the database should look like: https://imgur.com/a/XYA5yyn
I had to manually to make the entries in the database for the above image example. I don't want to have to generate this manually every time, say I make a mistake and have to rollback the migrations for example.
I am using a factory to generate this data. So the factory name is StepCategoriesFactory.php and clearly I'm calling the factory with the create() method from the DatabaseSeeder.php file.
I thought about doing this in a for loop, then i got as far as realizing when i called the 'step_id' => App\Model::all()->random()->id to grab a new id, that I wouldn't be able to ensure I wasn't grabbing the id that i just generated 5 entries for. I'm really new with Laravel, and I'm not sure where to even start on this. There's no real information on SO where faker can use the unique with another column. How would I Go about this?
NOTE: The step id is not always going to be 1-12. The step ID might be different depending on whether a step gets deleted and remade. So just assigning the step_id to equal 1-12 wont work.
UPDATE: Here's some code I just wrote, and I think I'm on the right track. Maybe. I've grabbed the step_id by it's number field as that will always be 1-12, and I've grabbed the IID out of the entry. But now I'm stuck on how to generate the order 1-5 without repeating itself. I still haven't run this yet as its incomplete and I know it'll throw an error without the correct order number.
UPDATE 2: I think I'm on the right track here. However I'm getting an undefined variable error. When I put the first line from within the anonymous function, it's resetting the order to "1" for every entry. How do i make the $autoIncrement variable available to the anonymous function? The Seeder has stayed the same between updates.
Image of the error: https://imgur.com/a/ywkd0Lb
Second image with the Die/Dump error in terminal: https://imgur.com/a/rOGRv32
Reference this article here: https://laracasts.com/discuss/channels/laravel/model-factory-increment-value-faker?page=1
UPDATE 3: I forgot the use ($autoIncrement) line of code for the anonymous function. Code below has been updated, but now I'm getting a different error saying that the order column has a null value and can't be inserted. clearly it should be '1'. Even after I call my $autoIncrement->next(); which should increment it to '1' it's still returning null according to the terminal. However, when I do a diedump on $autoIncrement->current() it's returning 1. Weird.
Update 3 error: https://imgur.com/a/STOmIjF
StepCategoriesFactory.php
use Faker\Generator as Faker;
$autoIncrement = autoIncrement();
$factory->define(App\StepCategory::class, function (Faker $faker) use ($autoIncrement) {
// Generate Created At and Updated at DATETIME
$DateTime = $faker->dateTime($max = 'now');
$autoIncrement->next();
$order = (int) $autoIncrement->current();
return [
// Generate Dummy Data
'order' => $order,
'name' => $faker->words(4, true),
'created_at' => $DateTime,
'updated_at' => $DateTime,
];
});
function autoIncrement()
{
for ($i = 0; $i < 5; $i++) {
yield $i;
}
}
Edit: Put a bounty on this question, as I think it would be helpful for the community to get a detailed answer. I'm looking for help to explain how to go about making sure I'm grabbing the same entry through each loop.
FINALLY SOLVED!
So I took in everyone's answers, and thought long and hard about using a for loop to create the order number. 1-5. The problem that I was running into at the end was that the $i variable was not resetting. So after the yield I had to check if the $i variable equalled 5 and then reset it back to zero.
Heres the code!
StepCategories.php
use Faker\Generator as Faker;
$autoIncrement = autoIncrement();
$factory->define(App\StepCategory::class, function (Faker $faker) use ($autoIncrement) {
// Generate Created At and Updated at DATETIME
$DateTime = $faker->dateTime($max = 'now');
// Get the next iteration of the autoIncrement Function
$autoIncrement->next();
// Assign the current $i value to a typecast variable.
$order = (int) $autoIncrement->current();
return [
// Generate Dummy Data
'order' => $order,
'name' => $faker->words(4, true),
'created_at' => $DateTime,
'updated_at' => $DateTime,
];
});
function autoIncrement()
{
// Start a loop
for ($i = 0; $i <= 5; $i++) {
// Yield the current value of $i
yield $i;
// If $i is equal to 5, that must mean the start of a new loop
if($i == 5) {
// Reset $i to 0 to start over.
$i = 0;
}
}
}
DatabaseSeeder.php
// Generate Dummy Categories
// Run the factory 12 times
foreach(range(1, 12) as $i) {
// Generate 5 entries each time
factory(App\StepCategory::class, 5)->create([
// Since all steps have a number 1-12 grab the step by the number column and get it's ID
'step_id' => App\Step::where('number', '=', $i)->first()->id,
]);
}
Thanks to all who helped!
Sorry if you don't understand my point so I'll try to explain it in code
use Illuminate\Database\Seeder;
$factory->define(App\StepCategory::class, function (Faker $faker) {
// Generate Created At and Updated at DATETIME
$DateTime = $faker->dateTime($max = 'now');
$step_id = function () {
return factory('App\Step')->create()->id;
};
return [
// Generate Dummy Data
'step_id' => $step_id,
'order' => uniqueOrder($step_id),
'name' => $faker->words(4, true),
'created_at' => $DateTime,
'updated_at' => $DateTime,
];
});
function uniqueOrder($step_id)
{
$unique = rand(1,5);
do {
$unique = rand(1,5);
}
while(StepCategory::where('step_id', $step_id)->andWhere( 'order', $unique)->exists())
return $unique;
}
for example if your Step model name is Steps :
$allSteps = Steps::all();
foreach($allSteps as $step){
for($i=1;$i<6;$i++){
//insert to table $step->id , $i for example
DB::table('yourTableName')->insert([
'name'=>'Step '.$step->id.'- Category '.$i ,
'order'=>$i ,
'step_id'=>$step->id
]);
}
}

Laravel You requested 1 items, but there are only 0 items available

I am getting row's column randomly to seed database, using eloquent :
$physician = SelectOption::where('select_option_group_id', 1)->pluck('name')->random();
it works if data exists in select_options table. But if it does not exists, it gives an error :
You requested 1 items, but there are only 0 items available.
I want to leave it empty, if it's empty.
Check if collection is not empty prior doing random():
$collection = SelectOption::where('select_option_group_id', 1)->pluck('name');
if (!$collection->isEmpty()) {
$physician = $collection->random();
} else {
...
}
Use inRandomOrder() instead:
$physician = SelectOption::where('select_option_group_id', 1)->inRandomOrder()->first();
$name = is_null($physician) ? 'No data available' : $physician->name;
Check the order of your seeder or the date of your migration file. The migrations file is executing by date ex: 20220429_16_43_02_create_order_table.php. So, if you have 20220416_.... after 20220429_.... that throw the exception.
Solution: Change (rename) the date of the file.

Laravel DB first() "Trying to get property of non-object"

I run a query using Laravel's DB first() which returns an object, when I check using dd() or vardump(). But when I try to print the value using echo ($promotion->pp_name); it gives error, but same property shows while dd($promotion->pp_name);
<?php dd($promotion->pp_name); ?> prints "urgent"
<?php echo ($promotion->pp_name); ?> but it gives "Trying to get property of non-object"
Full object dump results: <?php dd($promotion); ?>
{#196 ▼
+"ppo_id": 23
+"ppo_prj_id": 68
+"ppo_pp_id": 4
+"ppo_updated_date": "2014-05-20"
+"ppo_status": 1
+"pp_id": 4
+"pp_name": "urgent"
+"pp_dispText": "I want my project to be marked as an urgent project"
+"pp_amount": "5.00"
+"pp_updated_date": "2013-08-09"
+"pp_status": 1
}
and the function that return this object.
function getProjectPromotion($value='')
{
$project_id = $value;
$promotion = DB::table('project_promotion_option')
->join('project_promotion', 'project_promotion_option.ppo_pp_id', '=', 'project_promotion.pp_id')
->where('ppo_prj_id', '=' , $project_id )
->first();
return $promotion;
}
Are you calling this method in a loop? When you do a dd(), the script stops with the right result after the first loop run and all is good. But when you do echo in a loop, it continues and my guess is that at some point you pass some corrupted data that breaks the method.
We would need to see the code excerpt where you are calling the mentioned method to verify this hunch.
As Tadas Paplauskas suggest because the function call the query run in foreach each loop and in some iteration DB::first() method return null so echo gets error. i solved this by checking first if the value is set.
<?php if (isset($promotion->pp_name)) {
echo $promotion->pp_name;
} ?>

Laravel 5 eloquent model won't let me update

I do have the invitations table set up and in the database. I use it for other purpose such as adding more...
my goal: update the first record with a new value to a field:
I tried:
$invitation = new MemberInvitation();
$invitation1 = $invitation->find(1);
$invitation1->status = 'clicked';
$invitation1->save();
And also:
$invitation1 = \App\Model\MemberInvitation::find(1);
$invitation1->status = 'clicked';
$invitation1->save();
both ended with:
Creating default object from empty value
EDIT:
This piece of code worked and updated my records correctly -- I just can't do it via Eloquent's model:
\DB::table('member_invitations')->where('id', '=', $member_invitation->id)
->update(array('status' => 'clicked', 'member_id' => $member->id));
what am I missing?
Try this.
$invitation = MemberInvitation::findOrFail(1);
$invitation->status = 'clicked';
$invitation->save();
If that doesnt work, please show your model
find(1) doesn't mean "give me the first record", it means "give me the first record where id = 1". If you don't have a record with an id of 1, find(1) is going to return null. When you try and set an attribute on null, you get a PHP warning message "Creating default object from empty value".
If you really just want to get the first record, you would use the first() method:
$invitation = \App\Model\MemberInvitation::first();
If you need to get a record with a specific id, you can use the find() method. For example, to translate your working DB code into Eloquent, it would look like:
$invitation = \App\Model\MemberInvitation::find($member_invitation->id);
$invitation->status = 'clicked';
$invitation->member_id = $member->id;
$invitation->save();
The answer is obvious based on your reply from May 15th.
Your "fillable" attribute in the model is empty. Make sure you put "status" in that array. Attributes that are not in this array cannot up modified.

Yii date, hour, minute -> datetime string -> MySQL datetime

I'm using Yii to construct a web application. One of my input forms has a CJuiDatePicker for the date. I have two drop down lists, one for the hours, and one for the minutes.
My problem is in the data model, where I'm trying to convert the date, hour, and minute from the form into a MySQL datetime string. I have to produce a datetime string that looks like this - 2011-02-27 20:11:56, so Yii can convert the string into a MySQL datetime and insert the value into the row.
In the model, I have a rule that looks like this:
array('event_datetime_from', 'createDatetime',
'date'=>'event_date_from', 'hour'=>'event_hour_from',
'minute'=>'event_minute_from'),
The createDateTime validator function looks like this:
public function createDatetime($attribute, $params) {
if (!$this->hasErrors()) {
$date = $this->$params['date'];
$hour = $this->$params['hour'];
$minute = $this->$params['minute'];
if (trim($date) === '') {
$this->$attribute = null;
} else {
$parse = CDateTimeParser::parse(
$date.' '.$hour.':'.$minute,
'MM/dd/yyyy hh:mm');
$this->$attribute = date('Y-m-d H:i:s', $parse);
}
}
}
Now, I'm not a PHP developer. However, it appears to me that $params['date'] is returning the string value event_date_from, rather than the value of event_date_from.
My PHP question is, how do I get the value of event_date_from inside the createDateTime validator function?
My apologies if I overlooked the answer somewhere in the Yii documentation. I couldn't find many examples of validator functions. The Yii validator classes have a different parameter signature than validator functions.
Edited based on thaddeusmt's answer:
I tried extending CActiveRecord and coded an afterValidate method, but I couldn't find a place to define my working date, hour, and minute variables. I defined them in the extended method, and the afterValidate method couldn't see them. I got a PHP undefined variable error in the afterValidate method.
In the controller, I coded the following function:
protected function createDateTime($dateString, $hour, $minute) {
if (trim($dateString) == '') {
return null;
} else {
$timeString = $dateString.' '.$hour.':'.$minute;
return date('Y-m-d H:i:s', strtotime($timeString));
}
}
It should be a cinch to call a function in PHP, right?
I tried both of these calls in the actionCreate() function:
$model->event_datetime_from =
createDateTime($_POST['event_date_from'],
$_POST['event_hour_from'],
$_POST['event_minute_from']
);
and:
$model->event_datetime_from =
createDateTime($model->event_date_from,
$model->event_hour_from,
$model->event_minute_from
);
My controller code dies with either of these calls, and I get a blank (no HTML) response page.
I thought what I wanted to do was pretty simple. I want to take a date, hour, and minute string, and convert the concatenation to a datetime string. What am I missing?
What I do is, in the POST action in the Controller (where the POST vars are assigned), I convert the posted date and time values into a MySQL datetime with the date() and mktime() function, then validate/save. So, here is an example of the post action:
public function actionUpdate() {
$model=$this->loadModel();
if(isset($_POST['Model'])) {
$model->attributes = $_POST['Model']; // assign the rest of the POST vars here
$model->event_datetime_from = date(
'Y-m-d H:i:s', // convert the timestamp to the mySQL format
mktime( // create the timestamp from the posted date and time vars
$_POST['my-hour-var'], // set the hour
$_POST['my-minute-var'], // set the min
$_POST['my-second-var'], // set the sec
date("m"), // set the month
date("d"), // set the day
date("Y") // set the year
)
); // create a MySQL Y-m-d H:i:s format date from the POST vars
$model->save(); // this run the validation rules, naturally
}
}
(This assumes a model called "Model", POSTed hour, minute and second variables called my-hour-var, my-minute-var and my-second-var respectively, and that you are setting the DATE part to today.)
And here is an example of validation rule in the Model model using the CTypeValidator:
public function rules() {
return array(
array('event_datetime_from', 'type', 'type'=>'datetime', 'datetimeFormat'=>'yyyy-MM-dd hh:mm:ss', 'message' => '{attribute} is not a date and time!'),
}
I hope this helps!
I'd highly recommend checking out this extension:
http://www.yiiframework.com/extension/i18n-datetime-behavior/
It does some of this behavior automatically. You may need to update it a bit depending on how you expect your incoming dates to look. One way is to always run the property through strtotime() (built in php date parsing function) instead of the specific date parser in the extension.
I finally got my date, hour, and minute strings to convert to a datetime string.
Here's the code that I put in the actionCreate method and the actionUpdate method of the CalendarEventController:
$model->attributes=$_POST['CalendarEvent'];
// new code
$timeString = $_POST['CalendarEvent']['event_date_from'].' '.
$_POST['CalendarEvent']['event_hour_from'].':'.
$_POST['CalendarEvent']['event_minute_from'];
$model->event_datetime_from =
date('Y-m-d H:i:s', strtotime($timeString));
if (trim($_POST['CalendarEvent']['event_date_to']) === '') {
$model->event_datetime_to = null;
} else {
$timeString = $_POST['CalendarEvent']['event_date_to'].' '.
$_POST['CalendarEvent']['event_hour_to'].':'.
$_POST['CalendarEvent']['event_minute_to'];
$model->event_datetime_to =
date('Y-m-d H:i:s', strtotime($timeString));
}
I had two datetime fields in this model, with the second field optional. This code didn't work when I put it in a function. It only worked when I put the code inline in the two action methods.
I guess I don't understand PHP function calls. But, in case anyone else comes across this question, here's the answer that worked.

Categories