Laravel: saving multiple models at once / json nested input - json

I am submitting a POST request to my API endpoint using Postman.
In my nested JSON, an artist has one or more albums and each album has one or more songs.
I have two questions:
1) How do I perform nested array validation in Laravel? I am looking for an optimal / standard Laravel way to do so.
2) How do I save multiple models together?
Note: I did create the relationships in my Eloquent models, such as
class Artist extends Eloquent {
public function albums()
{
return $this->hasMany('Album');
}
}
class Album extends Eloquent {
public function songs()
{
return $this->hasMany('Song');
}
}
class Song extends Eloquent {
public function album()
{
return $this->belongsTo('Album');
}
}
class Album extends Eloquent {
public function artist()
{
return $this->belongsTo('Artist');
}
}

1) Use the validator's each method:
$validator = Validator::make(Input::all(), [...rules...]);
$validator->each('albums', [...rules...]);
2) After creating the artist, loop through your albums and call create on the relationship:
$artist = Artist::create(Input::all());
foreach (Input::get('albums') as $album)
{
$artist->albums()->create($album);
}

Related

laravel morphToMany of parent model how to use in subquery?

Current laravel models relations: ParentModel can have many documents, ChildModel can have many documents, same Documents can be belonged to any of ParentModel and ChildModel.
Also ChildModel always belongs to one ParentModel. ParentModel can have multiple ChildModels.
App\ParentModel relationships
...
public function childmodels()
{
return $this->hasMany('App\ChildModel');
}
public function documents()
{
return $this->morphToMany('App\Document', 'documentable');
}
...
App\ChildModel relationships
...
public function parentmodel()
{
return $this->belongsTo('App\ParentModel');
}
public function documents()
{
return $this->morphToMany('App\Document', 'documentable');
}
...
App\Document
...
public function parentmodels()
{
return $this->morphedByMany('App\ParentModel','documentable');
}
public function childmodels()
{
return $this->morphedByMany('App\ChildModel','documentable');
}
...
Now I'm trying to get all records of ChildModels (1) which have documents with specific type and (2) its ParentModel can also have documents.
The (1) first goal can be reached with such ChildModel method.
/* checking GET param to join this condition to final query */
if($request->has('report') && $request->input('report') == 'on') {
/* get all documents related to CurrentModel */
$query->whereHas('documents',function (Builder $query) {
$query->where('type', 1);
});
}
But this obviously doesnt include records of ChildModels, ParentModel of which has documents with specific type.
So the question is: how to include such condition into ChildModel query builder?
Found the easy way to access relations in builder
$query->whereHas('parentmodels.documents', function(Builder $query){
$query->where('type',1);
});

Laravel Relations One to One and One to Many

I have three models Class, Students and Studentinfo. Class and students are in a One to Many relationship and Sudents and studentinfo are in a one to one relationship.
While getting students from certain Class I get a list of data in an array.
What is the best way to get data from studentinfo for each student in the array?
I am trying to get this data in json format.
You'd set up relationships like the following on the models, the important one being the hasManythrough relation:
// Class.php
public function students() {
return $this->hasMany(Student::class);
}
public function studentInfo()
{
return $this->hasManyThrough(StudentInfo::class, Student::class);
}
// Student.php
public function studentInfo() {
return $this->hasOne(StudentInfo::class);
}
public function classes() {
return $this->belongsToMany(Class::class);
}
// StudentInfo.php
public function student() {
return $this->belongsTo(Student::class);
}
... you may cast a model or collection to a string, which
will automatically call the toJson method on the model or collection:
$json = (string)$class->studentInfo;
Laravel Docs: Serialization

Adding withCount to collection json

So i've got two models, Client and Project, and Client has a hasMany relationship with projects. I'm just trying to add the project count for the client into the JSON response but I can't get it to work.
My controller just returns all the projects;
public function index(Client $client)
{
return $client->all();
}
And my model contains the below;
protected $appends = ['client_projects_count'];
/**
* Create a relationship between this client and it's projects
*/
public function clientProjects() {
return $this->hasMany('App\Project');
}
public function getClientProjectsCountAttribute()
{
return $this->withCount('clientProjects');
}
This just adds client_projects_count to the response but it's an empty array. I'm close, if I dd($this->withCount('clientProjects')->get()) I can see the client_projects_count with the correct count, but if I remove the dd and keep the ->get() then I get an internal server error.
Also, it is possible to only load this for the index() method rather than every one?
From the Documentation
$clients = Client::withCount('projects')->get();
foreach ($clients as $client) {
echo $client->projects_count;
}
So I managed to resolve it myself, although I'm sure their must be a nicer way.
Client.php
protected $appends = ['client_projects_count'];
/**
* Create a relationship between this client and it's projects
*/
public function clientProjects() {
return $this->hasMany('App\Project');
}
public function clientProjectsCount()
{
return $this->clientProjects()->selectRaw('client_id, count(*) as aggregate')->groupBy('client_id')->get();
}
public function getClientProjectsCountAttribute()
{
return isset($this->clientProjectsCount()[0]) ? $this->clientProjectsCount()[0]->aggregate : 0;
}

Querying 'across' pivot table

I have two models, beers and distributions, which have a many-to-many relationship. The pivot model hasMany kegs, which contain some relevant information to the beer such as pricing and status. When I build my beer index, I need all the information of the beer model, the distributor model, and the keg model. What I am trying to figure out is how to query for all the information in an efficient manner. Here is my current query:
Keg's are scoped on status:
public function scopeStatus($query, $status)
{
return $query->where('status', '=', $status);
}
and I build my beers index with:
$kegs = Keg::status($status)->get();
$beers=[];
foreach ($kegs as $keg){
$beer = Beer::find($keg->beer_distribution->beer_id);
$distributor = Distributor::find($keg->beer_distribution->distributor_id);
$beers[]=[
'beer' => $beer,
'keg' => $keg,
'distributor' => $distributor];
}
return $beers;
I know that this is a slow query but im not sure how to do this in a single query. Is there a way that I can run this faster?
Some relevant model code:
class Beer extends Eloquent {
public function distributors()
{
return $this->belongsToMany('Distributor', 'beer_distributions');
}
class BeerDistribution extends Eloquent {
protected $fillable = ['beer_id', 'distributor_id'];
public function kegs()
{
return $this->hasMany('Keg', 'beer_distribution_id');
}
class Distributor extends Eloquent {
public function beers()
{
return $this->belongsToMany('Beer', 'beer_distributions');
}
class Keg extends Eloquent {
public function scopeStatus($query, $status)
{
return $query->where('status', '=', $status);
}
public function beerDistribution()
{
return $this->belongsTo('BeerDistribution');
}
}
So I figured out that what I really needed to do was add my query building relations on my Keg model (which was the fatherest 'down' in the nest of relations), and then use eager loading!
I now build my beers index like so:
$beers=[];
foreach (Keg::status($status)
->with('kegsize',
'beerDistribution.beer.brewery',
'beerDistribution.beer.style',
'beerDistribution.distributor')->get() as $keg){
$beers[]=$keg;
}
return $beers;
This brings me down to a stunning total of 10 queries.

Get all the attributes related to one Model as JSON

I have this model for Author:
<?php
class Author extends Eloquent {
public function albums()
{
return $this->belongsToMany('Album')->withPivot('city');
}
}
And this model for Album:
<?php
class Album extends Eloquent {
public function artist()
{
return $this->belongsTo('Artist');
}
public function authors()
{
return $this->belongsToMany('Author')->withPivot('city');
}
}
To get a model I can easily do this:
$author = Author::where('name','=','Chester Bennington')->first()->toJson();
and it'll return something like this (as JSON):
{"id":3,"name":"Chester Bennington","created_at":"2014-06-29 16:10:21","updated_at":"2014-06-29 16:10:21"}
See that it won't return the albums related to it. To get the albums I would have to do this: $author->albums->toJson()
Since I'm doing an API and it returns everything as JSON, is there a way to get the model and ALL its attributes without specifying which ones to get??
If you eager load the relationships you want, it'll be included in your JSON:
Author::with('albums')->where('name', 'Chester Bennington')->first()->toJson();