LARAVEL framework and Eloquent ORM model - laravel

{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
Above is a 'books' table
Using LARAVEL framework and Eloquent ORM model, how to return the following result:
{ "author" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "author" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
I am learning Laravel.

You could use GROUP_CONCAT.
Book::groupBy('author')
->select(DB::raw('GROUP_CONCAT(title) as books'), 'author')
->get()
->map(function ($author) {
$author->books = preg_split('/,/', $author->books);
return $author;
});
This will get you:
{ "author" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "author" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }

If the table structure is within your control, then the better solution would be to place the data into two tables books and authors, to avoid data duplication as is the case now with a single table.
Then define two Eloquent Models and setup a one-to-many relationship. As in one author can have many books written.
So the Book model might look like this:
class Book extends Model
{
public function author()
{
return $this->belongsTo(Author::class);
}
}
And the Author model might look like this:
class Author extends Model
{
public function books()
{
return $this->hasMany(Book::class);
}
}
Now you can use the relationship, eager loading and a little collection processing to fetch the data in your preferred format:
$authors = Author::with('books')->get()->map(function ($author) {
return [
'author' => $author->name,
'books' => $author->books->pluck('title')
];
});
You might also want to read up on Database Normalization, and in your case more specifically The 1st Normal Form.

Related

MongoDB: How to update an item in an array using laravel

In mongoDB, I have a table product :
{
"_id" : ObjectId("61e93a99667ad949974948fb"),
"listProduct" : [
{
"_id" : "61e93aceebadb38d243fa321",
"name" : "Product 1",
"order" : 1,
"childProduct" : [
{
"_id" : "61e93aceebadb38d243fa322",
"name" : "Samsung",
"description" : "New Samsung",
"productOn" : 1676351448
},
{
"_id" : "61ee808140451a00b4eee93c",
"name" : "Iphone",
"description" : "New Iphone",
"productOn" : 1676605213
},
],
}
],
}
In Models:
<?php
namespace App\Models\Backend\Mongo;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;
class Product extends Eloquent
{
protected $connection = 'mongodb';
protected $collection = 'product';
protected $guarded = ['_id'];
protected $dateFormat = 'U';
protected $fillable = [
'listProduct',
];
}
And in the laravel controller, I update the productOn field for it:
use App\Repositories\MongoDB\ProductRepository;
protected $productRepository;
public function __construct(
ProductRepository $productRepository,
) {
$this->productRepository = $productRepository;
}
public function updateProductOn()
{
$product = $this->productRepository->getModel()::where('_id', '=', '61e93a99667ad949974948fb')->first();
if ($product) {
$product->listProduct[0]['childProduct'][0]['productOn'] = strtotime('now');
$product->save();
}
}
And when I run that function, and save it, it gets the following error:
"message": "Indirect modification of overloaded property App\\Models\\Mongo\\Product::$listProduct has no effect",
"exception": "ErrorException",
I have researched and learned how to product->push and I have followed it, but it still doesn't work.And I also know push to save an array.
$product->push("listProduct[0].childProduct[0].productOn", strtotime('now'));
or
$product->push("listProduct[0]['childProduct'][0]['productOn']", strtotime('now'));
Is there any way to update productOn, please give me any suggestions. Thanks.

Update data to mongoBD failed at updatedAt in laravel

I am doing update function to mongoDB in laravel. And I get some errors like this.
For example I have 1 table in mongoBD which is products:
{
"_id" : ObjectId("5e3a80d6d50c7d6f1a15b582"),
"status" : 1,
"name" : "Product 1",
"slug" : "product-1",
"description": 'test product 1',
"createdAt" : 1658905822,
"updatedAt" : 1659407411
}
In controller :
use App\Models\Product;
public function update(Request $request)
{
$reqProduct = $request->data;
// dd($reqProduct) result :
// array(65) {
// ["_id"]=>
// string(24) "5e3a80d6d50c7d6f1a15b582"
// ["status"]=>
// int(1) 1
// ["name"]=>
// string(11) "Product 1"
// ["slug"]=>
// string(11) "product-1"
// ["description"]=>
// string(5) "sss"
// ["createdAt"]=>
// string(10) "1658905822"
// ["updatedAt"]=>
// string(10) "1678537691"
// }
$product = Product::where('_id', '=', $reqProduct['_id'])->first();
$product->update($reqProduct);
}
So the update was successful. But my database sometimes come up with a case, updatedAt when saving in int type, but sometimes it is stored in date type (ISODate).
Example :
{
"_id" : ObjectId("5e3a80d6d50c7d6f1a15b582"),
"status" : 1,
"name" : "Product 1",
"slug" : "product-1",
"description": 'test product 1',
"createdAt" : 1658905822,
"updatedAt" : ISODate("2013-10-01T00:00:00.000Z")
}
And when I click update the error message: Object of class Carbon\\Carbon could not be converted to int.
I tried :
$product = Product::where('_id', '=', $reqProduct['_id'])->first();
$product->unset('updatedAt', $product->updatedAt);
$product->update($reqProduct);
But can't. Everyone please give me the solution so I can fix it, Thanks.
Simply convert it to datetime string before updating.
$updated_at = Carbon::createFromTimestamp(1678537691)->toDateTimeString();
["updatedAt"] => $updated_at,

How to change the structure of eager loaded data efficiently in Laravel

I'm loading a product eagerly with its relationship data in Laravel.
$product = Product::with('attributeValues.attribute')->find($id)->get();
Currently I get the response structure as follows.
[
{
"product_id": 1,
"product_name": "Shirt A",
"attribute_values": [
{
"attribute_value_id": 1,
"attribute_value": "small",
"attribute": {
"attribute_id": 1,
"attribute": "size"
}
},
{
"attribute_value_id": 1,
"attribute_value": "medium",
"attribute": {
"attribute_id": 1,
"attribute": "size"
}
},
...
]
},
...
]
The structure I expected to get is as follows.
[
{
"product_id": 1,
"product_name": "Shirt A",
"attribute": [
{
"attribute_id": 1,
"attribute": "size",
"attribute_values": [
{
"attribute_value_id": 1,
"attribute_value": "small"
},
{
"attribute_value_id": 1,
"attribute_value": "medium"
}
]
},
...
]
},
...
]
The current relationships of the models are as follows
class Product extends Model {
public function attributeValues(){
return $this->belongsToMany(AttributeValue::class, 'product_attributes');
}
}
class AttributeValue extends Model {
public function attribute(){
return $this->belongsTo(Attribute::class);
}
}
class Attribute extends Model { }
Currently I'm succeeded getting this structure correctly by using product and getting it attributes separately using raw queries. I'm trying to achieve a Eloquent way of doing this task.
The tables I have are as follows
products
+----+------+
| id | name |
+----+------+
product_attributes
+----+------------+--------------------+
| id | product_id | attribute_value_id |
+----+------------+--------------------+
attribute_values
+----+--------------+-------+
| id | attribute_id | value |
+----+--------------+-------+
attributes
+----+-----------+
| id | attribute |
+----+-----------+
you can use with inside with using custom eager loading:
$product = Product::with(['attributeValues'=>function($query){
$query->select(['attribute_value_id','attribute_value'])
->with('attribute:attribute_id,attribute');
}])->find($id)->get();
What about using collections methods ? I'm not an expert but i would have done something like this :
$products->map(function($product){
$attribute = $product->attribute_values->groupBy('attribute.attribute')->map(function($groupAttribute,$key){
$attribute_values = $groupAttribute->map(function($attribute) {
return ['attribute_value_id'=>$attribute->attribute_value_id,'attribute_value'=>$attribute->attribute_value];
});
return ['attribute_id' => 1 ,'attribute'=> $key , 'attribute_values' => $attribute_values ];
});
return [
'product_id'=>$product->product_id,
'product_name'=>$product->product_name,
'attribute'=>$attribute
];
});
Just do the following you will get the required result.
$product = Product::with('attribute.attributeValues')->find($id)->get();
The best way is to use JsonResource.
Here you can find a short and easy example
Just define ProductResource, attribyteValueResource, and AttributeResource
Here is the example:
PropertyResource:
use Illuminate\Http\Resources\Json\JsonResource;
class PropertyResource extends JsonResource
{
public function toArray($request): array
{
return [
'product_id' => $this->product_id,
'product_name' => $this->product_name,
'attribute_values' => AttributeResource::collect( $this->whenLoaded('attributeValues') ),
];
}
}
AttributeValueResource:
use Illuminate\Http\Resources\Json\JsonResource;
class AttributeValueResource extends JsonResource
{
public function toArray($request): array
{
return [
'attribute_value_id' => $this->attribute_value_id,
'attribute_value' => $this->attribute_value,
'attribute' => AttributeValueResource::make( $this->whenLoaded('attribute') ),
];
}
}
AttributeResource:
use Illuminate\Http\Resources\Json\JsonResource;
class AttributeResource extends JsonResource
{
public function toArray($request): array
{
return [
'attribute_id' => $this->attribute_id,
'attribute' => $this->attribute,
];
}
}
Then just use them:
$product = ProductResource::make(
Product::load('attributeValues.attribute')->find($id)
);

Laravel. How to insert nested data into database

It's very easy to insert a register when I have a JSON to insert an Article like this:
--JSON to insert--
{
"title" : "a title",
"content" : "some content",
"user_id" : 3
}
To put it short, I create the route aiming to the store method of the controller and go for something simple like:
--ArticleController--
public function store(Request $request)
{
$article = Article::create($request->all());
return response()->json($article, 201);
}
But, what is the correct way to insert the data to store if I have something JSON:API complying like this?:
{
"data":
{
"type": "articles",
"attributes": {
"title": "a title",
"content" : "some content",
"user_id" : 3
}
}
}
What is the correct approach?
I can't believe it was so easy,
I just changed this:
$article = Article::create($request->all());
return response()->json($article, 201);
for this:
$article = Article::create($request->data['attributes']);
return response()->json($article, 201);
And that's it

Fail to try insert data in a one-to-many relationship with additional fields

I'm trying to create a record from the parent relationship.
My models
use Illuminate\Database\Eloquent\Model;
class Cheque extends Model
{
protected $fillable = [
"numero",
"banco",
"factura_id",
];
public function factura()
{
return $this->belongsTo('App\Factura');
}
}
class Factura extends Model
{
protected $fillable = [
'cliente_id',
'cotizacion_id',
'sub_total',
'porcentaje_descuento',
'descuento_total',
'total',
'iva',
];
public function cheque()
{
return $this->hasMany('App\Cheque');
}
This is my code in FacturaController
$factura = Factura::create($request->all());
$factura->cheque()->create($request->cheque);
The request:
{
"cliente_id" : "3",
"nombre" : "gustavo",
"sub_total" : 20000.50,
"porcentaje_descuento" : 20,
"descuento_total" : 50,
"total" : 100,
"iva" : 12,
"cheque" : {
"1" : {
"numero" : "25525886",
"banco" : "banesco"
}
}
}
The 'cheque' is created but the fields 'numero' and 'banco' are left blank.
What am I doing wrong?
Thanks in advance
$factura->cheque($factura_id)->create($request->cheque);
Try this.
$factura = Factura::create($request->all());
$cheque = Cheque::create($request->only(['input', 'inputenter code here']);
$cheque = factura_id = $factura-id;
$cheque->save();
it's something like this

Resources