Laravel Excel Row Validation with conditions depends on another field input - laravel

I need to validate every rows from excel user upload in laravel which already converted to numeric array.
The validation i wanted is :
to make sure that the 1st field (0) is not blank,
then check whether the input is 'PRODUCTION-PROJECT' or not, if yes, then the 2nd field is required (1).
how to achieve this ?
My Controller Import class
$file = $request->file('file');
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$reader->setLoadSheetsOnly(['Upload']);
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load($file->getRealPath());
$sheet = $spreadsheet->getActiveSheet();
$array = $sheet->toArray();
The array looks like this :
array:8 [
0 => array:11 [
0 => "PRODUCTION-PROJECT"
1 => "Consumable equipment expenses"
2 => "2022-07"
3 => "2022-08"
4 => "Forstr"
5 => "DOMESTIC"
6 => "ABCDE"
7 => "IDR"
8 => 2000
9 => 1
10 => "Enim temporibus est quis."
],
1 => array:11 [
0 => "PRODUCTION-PROJECT"
1 => null
2 => "2022-08"
3 => "2022-08"
4 => "RX"
5 => "DOMESTIC"
6 => "FGHIJ"
7 => "USD"
8 => 2000
9 => 1
10 => null
],
];
The validation i've tried so far like so :
$validatedData = Validator::make($array, [
'*.0' => 'required',
'*.1' => Rule::requiredIf('*.0' === 'PRODUCTION-PROJECT')
];
and the validation didn't show any error

The params of Rule::requiredIf should be a callback function that you need to custom the rule and input.
It's better to change Rule::requiredIf('*.0' === 'PRODUCTION-PROJECT') to 'required_if:*.0,PRODUCTION-PROJECT"'
so the correct code is :
$validatedData = Validator::make($array, [
'*.0' => 'required',
'*.1' => 'required_if:*.0,PRODUCTION-PROJECT'
];

Related

stream_get_contents in loop work half the time

In my controller I retrieve several boxes, and for the images of each box I do like this because I have the images in a blob column:
$donnees = $entityManager
->getRepository(Occasion::class)
->findBy(['isOnLine' => true], ['id' => "DESC"]);
$occasions = $paginator->paginate(
$donnees, /* query NOT result */
$request->query->getInt('page', 1), /*page number*/
12 /*limit per page*/
);
//on va stocker les images
$images = [];
foreach($occasions as $key => $occasion) {
$images[$key] = stream_get_contents($occasion->getBoite()->getImageBlob());
}
But when I dump $images I only have 1 image out of 2...
CataloguesController.php on line 137:
array:4 [▼
0 => "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwM ▶"
1 => ""
2 => "/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwM ▶"
3 => ""
]
Do you know why ? Thanks
???

Custom conditional validation laravel

I have a request data like this.
array:6 [
"teacherid" => "1"
"classid" => "7"
"schedule" => array:5 [
0 => "Monday - lesson 1"
1 => "Monday - lesson 2"
2 => "Monday - lesson 3"
3 => "Wednesday - lesson 3"
4 => "Wednesday - lesson 4"
]
"year" => "2021"
"smt" => "1"
"_token" => "tSFppSS2TWPQYbQofJD6pPufTLFNpKWAssZNGIHO"
]
and DB look like this
table schedules
I want to validate the input teacher, schedule and classroom like this:
if schedule, teacher and classroom are duplicate
or if schedule and teacher duplicate, but different classroom
I've tried with rule::unique as below but validation doesn't work.
how to make validation of point 1 and 2 above with rule::unique ?
$messages = ['schedule.*.unique' => 'Schedule :input already taken'];
$validator = \Validator::make(
$request->all(),
[
'schedule.*' => 'required',
Rule::unique('schedules')->where(function ($query) use ($request) {
return $query->where('schedule', $request->schedule);
->where('teacher_classes_id', $request->classid)
->where('teacher_subjects_id', $request->teasubid);
}),
],
$messages
);

HasOne showing either "null" or "Undefined property: Illuminate\\Database\\Eloquent\\Relations\\HasOne"

I'm trying to use hasOne to return a single value within my data set, however I can't seem to return the single column as a value without returning the full object.
What the object looks like when returned when you just return hasOne:
protected $with = ["steps"];
public function steps() {
return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id");
}
Result of just using hasOne as the default object:
array:21 [
"id" => 5887894545
"steps" => array:5 [
"id" => 21
"compare_id" => 588789
"steps" => array:12 [
0 => 1
1 => 2
2 => 3
3 => 4
4 => 13
5 => 6
6 => 7
7 => 17
8 => 8
9 => 9
10 => 10
11 => 12
]
"created_at" => "2021-10-05 08:48:44"
"updated_at" => "2021-10-05 08:48:44"
]
"created_at" => "2021-10-05 08:48:43"
"updated_at" => "2021-10-05 08:48:43"
"expired_at" => "2021-10-09 08:48:43"
"booked" => 0
"reference" => null
"utm" => ""
"updates" => []
]
Returns null:
array:21 [
"id" => 5887894545
"steps" => null
"created_at" => "2021-10-05 08:48:43"
"updated_at" => "2021-10-05 08:48:43"
"expired_at" => "2021-10-09 08:48:43"
"booked" => 0
"reference" => null
"utm" => ""
"updates" => []
]
Returns Call to a member function addEagerConstraints() on array:
public function steps() {
return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id")->value("steps");
}
Returns Undefined property: Illuminate\\Database\\Eloquent\\Relations\\HasOne::$steps:
public function steps() {
return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id")->steps;
}
Expected Result:
array:21 [
"id" => 5887894545
"steps" => array:12 [
0 => 1
1 => 2
2 => 3
3 => 4
4 => 13
5 => 6
6 => 7
7 => 17
8 => 8
9 => 9
10 => 10
11 => 12
]
"created_at" => "2021-10-05 08:48:43"
"updated_at" => "2021-10-05 08:48:43"
"expired_at" => "2021-10-09 08:48:43"
"booked" => 0
"reference" => null
"utm" => ""
"updates" => []
]
Update based on conversation in comments with #MaartenDev
I want to append the $model->steps->steps to $model->steps when the model gets called. As I'm updating database tables to split certain data into tables and want to keep the structure of the data the same when calling the model.
e.g. if you was using getUserCountAttribute you can easily return just the number by doing hasMany()->Count().
So i'm wanting to append the steps array to the steps property when the model is called.
Would using a custom getter that uses the steps relation be an option? You could preload the values using $appends.
class Model {
protected $appends = ["steps"];
protected $hidden = ['stepsRelation']
public function getStepsAttribute()
{
return $this->stepsRelation->steps;
}
private function stepsRelation() {
return $this->hasOne("App\Compares\ComparesSteps", "compare_id", "id");
}
}
Checkout the Laravel docs on appends.

Apply reduce method for all properties of an object

I have an object in Laravel that represent a monthly report.
0 => array:20 [▼
"id" => 43
"operation_id" => 1
"meter_id" => 3
"period" => "monthly"
"total_conso" => "103.42"
"total_autoconso" => "59.47"
"total_grid" => "43.95"
"bill" => "31.95"
"grid_fee" => "26.97"
"solar_turpe_tax_fee" => "4.99"
"savings" => "4.41"
"total_prod" => null
"total_surplus" => null
"autoconso_rate" => "57.5"
"autoprod_rate" => null
"surplus_rate" => null
"date" => "2019-08-24T00:00:00.000000Z"
"created_at" => "2019-08-24T00:00:00.000000Z"
"updated_at" => "2020-10-01T15:03:38.000000Z"
I have a array with 12 objects of these, one per month.
I am calculating the yearly report values, and I have to sum all 12 month for each field.
I can do it with reduce field by field with:
$totalConso = $reports->reduce(function ($sum, $report) {
return $sum + $report->total_conso;
}, 0);
What I am looking for is a way to do it for all fields. Is it possible ? It would allow me not to duplicate 10 times the same reduce function
Thanks !
You could do something like this:
[$totalConso, $totalAutoConso] = collect(['total_conso', 'total_autoconso'])->map(fn ($property) => $reports->sum($property));
If you would prefer an array with each total:
$totals = collect(['total_conso', 'total_autoconso'])->mapWithKeys(fn ($property) => [$property => $reports->sum($property)]);
This would give you a collection with all the totals.
If you don't like hardcoding the list of total_* attributes, you can get them dynamically from the list of fillable attributes of your model (this assumes you use the fillable property):
$totals = collect(Report::make()->fillable)
->filter(fn ($property) => strpos($property, 'total_') === 0)
->mapWithKeys(fn ($property) => [$property => $reports->sum($property)]);
Demo: https://laravelplayground.com/#/snippets/ec3c662f-0ab9-4de8-8422-7bed2f054677
Use the collect helper and sum method:
$total = collect($reports)->sum('total_conso');

Replicating models across 2 systems

I have 2 systems which are seperate from each other. To let them communicate I have built an API. Both systems have common Models, one of which is a Project Model with all its relationships.
Within System A, to send a Project with its relationships I do the following.
$client = new GuzzleHttp\Client();
$jsonProject = json_encode(Project::with('projectType', 'projectType.projectTypeData',
'projectAssets', 'projectAssets.projectAssetsData')->find($project->id));
$req = $client->request('POST', 'https://someurl/postProject', [
'body' => json_encode($jsonProject),
'headers' => [
'Content-Type' => 'application/json',
'Content-Length' => strlen($jsonProject),
]
]);
Within System B I have the routes set up, and when the above is posted the following is triggered
public function store(Request $request)
{
$project = $request->all();
dd($project);
}
When I make the post request from System A I see something like this because of the dump in System B (I have removed a lot of the output to cut down on code).
array:17 [
"id" => 3
"projectName" => "Test Project"
"user_id" => 1
"contact" => "John Doe"
"project_type" => array:7 [
"id" => 3
"project_id" => 3
"project_type_data" => array:1 [
0 => array:8 [
"id" => 5
"projectType" => "Standard"
]
]
]
"project_assets" => array:7 [
"id" => 2
"project_id" => 3
"project_assets_data" => array:4 [
0 => array:8 [
"id" => 5
"label" => "Logo"
"altTag" => ""
"urlPath" => ""
"projectAssetsId" => 2
]
]
]
]
So everything seems to work fine. My question is this. System B now has a load of json data containing all the data required to make the models. If I was able to send a model via the api (not json) then I could easily create the model within System B. Because it is json data however, and I cant decode it because it is not a string, do I have to start looping it all in order to make my models?

Resources