Laravel - best way to update huge amount of data from request? - php

I have an ecommerce application developed using laravel. Every day I want to update an amount of products from external api (request).
Response:
[
{
"id": 1,
"code": "0564",
"amount": 200
},
{
"id": 2,
"code": "4235",
"amount": 24
},
{
"id": 3,
"code": "27683",
"amount": 646
},
{
"id": 4,
"code": "457",
"amount": 44
},
]
Below laravel function in controller:
public function import(Request $request)
{
$products = $request->all();
foreach ($products as $product) {
$currentProduct = Product::where('code', $product['code'])->first();
if ($currentProduct) {
$currentProduct->amount = $product['amount'];
$currentProduct->update();
}
}
return response()->json([
'status' => 'success',
], Response::HTTP_OK);
}
Above code is working, but it is slower. Is there any best way to do this?

You Can Use
public function import(Request $request)
{
$products = $request->all();
foreach ($products as $product) {
Product::where('code', $product['code'])
->update(['amount'=>$product['amount']]);
}
return response()->json([
'status' => 'success',
], Response::HTTP_OK);
}

if you are using (Laravel >= 8.x) there is a method named upsert. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database.
try something like this:
public function import(Request $request)
{
$data = $request->all();
foreach (array_chunk($data, 500) as $products) {
$updateData = [];
foreach($products as $value) {
$currentProduct = Product::where('code', $value['code'])->first();
if ($currentProduct) {
$updateData[] = [
'code' => $value['code'],
'amount' => $value['amount'],
];
}
}
Product::upsert($updateData, ['code'], ['amount']);
}
return response()->json([
'status' => 'success',
], Response::HTTP_OK);
}

Related

Laravel get single item from Array in Query with relation ship

in my one of models such as posts i have a column array as featured_image and when i get result query from this model that return all of this array items, for example:
"featured_image": {
"images": {
"original": "/uploads/post_images/2020/1598873165.jpg",
"300": "/uploads/post_images/2020/300_1598873165.jpg",
"900": "/uploads/post_images/2020/900_1598873165.jpg"
},
"thumbnail": "/uploads/post_images/2020/300_1598873165.jpg"
},
now how can i modify this below query to get only one item from this array such as thumbnail?
public function allData()
{
$posts = Post::with(['groups', 'categories' => function ($query) {
$query->whereLocked(false);
}])->wherePublished(true)->get();
return response()->json([
'posts' => $posts,
], 200);
}
you can try this :
public function allData()
{
$posts = Post::with(['groups', 'categories' => function ($query) {
$query->whereLocked(false);
}])->wherePublished(true)
->select('{primary must included to use With}', 'other', 'columns', 'featured_image->thumbnail as thumbnail')
->get();
return response()->json([
'posts' => $posts,
], 200);
}

How to validate unique composite key of array data in laravel Validations

Request data
{
"exam_id": 10,
"exam_scores": [
{
"student_id": 1,
"subject_id": 1,
"marks": 50,
},
{
"student_id": 1,
"subject_id": 2,
"marks": 70,
},
{
"student_id": 1,
"subject_id": 3,
"marks": 80,
}
],
}
Where student_id and subject_id is a unique composite key,
how to make validator for composite key with Rule Method that accepts array of data ()
I tried but it does not work as expected.
$validator = Validator::make(request()->all(), [
"exam_id"=> "required|integer",
"exam_scores"=> "required|array",
'exam_scores.*.student_id' => [
Rule::unique('results')->where(function ($query) {
return $query
->whereStudent_idAndSubject_id(request()->get('exam_scores.*.student_id'),request()->get('exam_scores.*.subject_id'))
})
],
]);
below request should not validate data. but it validates successfully.
{
"exam_id": 10,
"exam_scores": [
{
"student_id": 1,
"subject_id": 1,
"marks": 50,
},
{
"student_id": 1,
"subject_id": 1,
"marks": 70,
}
],
}
Below request successfully validate data with single object of exam_scores that is expected.
{
"exam_id": 10,
"exam_scores": {
"student_id": 1,
"subject_id": 1,
"marks": 50,
}
}
$validator = Validator::make(request()->all(), [
"exam_id"=> "required|integer",
"exam_scores"=> "required|array",
'exam_scores.student_id' => [
Rule::unique('results')->where(function ($query) {
return $query
->whereStudent_idAndSubject_id(request()->get('exam_scores.student_id'),request()->get('exam_scores.subject_id'))
})
],
]);
After searching lots of Blogs, Tutorials & of course laravel Documentation, I got something that is solved my problem here is the blog link. that was not what I exactly wanted but it clears concept of what I have to do.
this guy saves my day.
Validate Dynamic request Values
Here is an example.
namespace App\Http\Requests;
use App\Http\Requests\Request;
class OrderRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = [
'name' => 'required|max:255',
];
foreach ($this->request->get('items') as $key => $val) {
$rules['items.' . $key] = 'required|max:10';
}
return $rules;
}
public function messages()
{
$messages = [];
foreach ($this->request->get('items') as $key => $val) {
$messages['items.' . $key . '.max'] = 'The field labeled "Book Title ' . $key . '" must be less than :max characters.';
}
return $messages;
}
}
The solution were pretty much simple and easy.

Laravel Api Resource

I'm trying to create an API for my data tables using Laravel's resource. I have three models with relationships. Every time I hit my api routes to check the result I'm getting a null value in my sub_specializations. Here's the result already JSON formatted.
{
"data":[
{
"first_name":"Rusty",
"last_name":"Ferry",
"specializations":{
"specialization_id":11,
"specialization_name":"Endocrinology"
},
"sub_specializations":null
},
{
"first_name":"Nadia",
"last_name":"Ondricka",
"specializations":{
"specialization_id":22,
"specialization_name":"ENT"
},
"sub_specializations":null
},
{
"first_name":"Erich",
"last_name":"Torphy",
"specializations":{
"specialization_id":2,
"specialization_name":"Cardiologist"
},
"sub_specializations":null
}
]
}
Here are all my resources. This the DoctorsResource
public function toArray($request)
{
return [
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'specializations' => new SpecializationsResource($this->specializations),
'sub_specializations' => new SubSpecializationsResource($this->sub_specializations),
];
}
Specializations Resource
public function toArray($request)
{
return [
'specialization_id' => $this->specialization_id,
'specialization_name' => $this->specialization_name,
];
}
SubSpecializations
public function toArray($request)
{
return [
'sub_specialization_id' => $this->sub_specialization_id,
'sub_specialization_name' => $this->sub_specialization_name,
'doctors' => new DoctorsResource($this->doctors),
];
}
Lastly, this is the controller
protected $user;
public function __construct(Doctors $doctors){
$this->doctors = $doctors;
}
public function index()
{
$doctors = $this->doctors->with('specializations', 'subSpecializations')->get();
return DoctorsResource::collection($doctors);
}
The result that I'm expecting is similar to this
{
"data":[
{
"first_name":"Rusty",
"last_name":"Ferry",
"specializations":{
"specialization_id":11,
"specialization_name":"Endocrinology"
},
"sub_specializations": {
"sub_specialization_name":"value"
}
}
]
}
You have to make sure there is data of Sub Specializations for particular doctor.
If there is data then add that data to Doctor Resource otherwise it will be blank.
Just need to change line in doctor Resource like:
'sub_specializations' => $this->sub_specializations !== null ? new SubSpecializationsResource($this->sub_specializations) : '',
You can do same thing with specializations also.

How to change json format using API Resource?

I am trying to push an item status into array and then return through API Resource, but API resource returning error. I am trying following script to do that
$Games = Game::all();
return new GameResource($Games);
And it is return as following
{
"data": [
{
"id": 1,
"name": "similique",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27"
}
]
}
I am trying following to achieve my desire json array
$Games = Game::all();
$DataArray = ['status' => 'success', 'data' =>$Games ];
return new GameResource($DataArray);
But it is returning error
Call to a member function toBase() on array in fil
My desire json array is following
{
"status": "success",
"data": [
{
"id": 1,
"name": "similique",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27"
}
]
}
In your GameResource.php change the toArray() method:
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
'status' => 'success', // Here goes the logic which checks for success or failure. However, this depends on what you consider as "success".
];
}
You can try this code:
$games = Game::get();
return response()->json(new GameResource($games));
data is a public property of your GameResource ? then your code should be
return ['status' => 'success', 'data' => (new GameResource($Games))->data ];
Brother, I am using this way.
Step 1: I create a generalOutput Resource.php and modify like below.
class GeneralOutputResource extends JsonResource
{
private $status;
private $data;
public function __construct($status, $data)
{
$this->status = $status;
$this->data = $data;
}
public function toArray($request)
{
return ['status' => $this->status, 'data' => $this->data];
}
}
Step 2:
Then I just call the class like this
$a_user = User::find(1);
return new GeneralOutputResource(1, $a_user);
Everytime I return the response, I will call the GeneralOutputResponse.

Laravel Dingo API - How to respond with multiple collections / transformers?

To initialize my app I have the following route:
/initialize
This returns Taxonomies, Enumerables and a couple of other taxonomy like collections. This saves multiple HTTP requests.
Although with Dingo / Fractal, I cannot see how I can respond with multiple collections?
e.g.
return [
'taxonomies' => $this->response->collection($taxonomies, new TaxonomyTransformer);
'enumerables' => $this->response->collection($enumerables, new EnumerableTransformer);
'otherStuff' => $this->response->collection($otherStuff, new OtherStuffTransformer);
];
return response()->json([
'data' => [
'taxonomies' => $this->fractal->collection($taxonomies, new TaxonomyTransformer);
'enumerables' => $this->fractal->collection($enumerables, new EnumerableTransformer);
'otherStuff' => $this->fractal->collection($otherStuff, new OtherStuffTransformer);
]
], 200);
This should return the JSON in the format you are looking for.
I have the same issue ,and I found the solution from How to use Transformer in one to many relationship. #1054.
Here is the collection I want to return with the transfomer of dingo in my controller.
$user = User::where('email','=',$input['email'])->with('departments')->with('roles')->get();
DepartmentTransformer
class DepartmentTransformer extends TransformerAbstract
{
public function transform($department)
{
return [
'id' => $department['id'],
'name' => $department['name'],
'level' => $department['level'],
'parent_id' => $department['parent_id']
];
}
}
RolesTransformer
class RolesTransformer extends TransformerAbstract
{
public function transform($role)
{
return [
'name' => $role['name'],
'slug' => $role['slug'],
'description' => $role['description'],
'level' => $role['level']
];
}
}
UserTransformer
class UserTransformer extends TransformerAbstract
{
protected $defaultIncludes = ['departments','roles'];
public function transform($user)
{
return [
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email'],
'phone' => $user['phone'],
];
}
public function includeDepartments(User $user)
{
$dept = $user->departments;
return $this->collection($dept, new DepartmentTransformer());
}
public function includeRoles(User $user)
{
$rl = $user->roles;
return $this->collection($rl, new RolesTransformer());
}
}
In my controller
$user = User::where('email','=',$input['email'])->with('departments')->with('roles')->get();
return $this->response->collection($user, new UserTransformer());
And I got the result
"data": {
{
"id": 43,
"name": "test7",
"email": "test7#foxmail.com",
"phone": "186********",
"departments": {
"data": {
{
"id": 1,
"name": "业务一部",
"level": 1,
"parent_id": 0
}
}
},
"roles": {
"data": {
{
"name": "agent",
"slug": "agent",
"description": "业务员",
"level": 1
}
}
}
}
}
Please take note of the usage of $defaultIncludes and includeXXX() methonds in the UserTransformer.You can get more detail info from Fractal Doc.

Categories