Laravel - replace null with empty array when no relation is found - php

Is it possible to replace null with an empty array when no relation is found?
E.g. The customer has contacts and contracts but one of the contract has no web.
$customers = Customer::with('contacts', 'contracts.web')
->orderBy('company')->orderBy('prename')->get();
The result would be as following...
2 => array:21 [
"id" => 1
"contacts" => array:2 [
0 => array:12 [
"id" => 1
"customer_id" => 1
]
1 => array:12 [
"id" => 2
"customer_id" => 1
]
]
"contracts" => array:2 [
0 => array:9 [
"id" => 1
"customer_id" => 1
"web" => array:7 [
"id" => 1
"contract_id" => 1
]
]
1 => array:9 [
"id" => 2
"customer_id" => 1
"web" => null // should be replaced with []
]
]
]
As I read in the docs (Constraining Eager Loads), it's only possible to manipulate the query with constraining eager loads.
UPDATE
Contract class
class Contract extends Model
{
public function web()
{
return $this->hasOne(Web::class);
}
}

For further readers here's an explanation how to solve this kind of problem.
Laravel returns an empty array if no records are found on a hasMany relation. If a hasOne relation is implemented, null will be returned.
So if you need an array also if no record is found on a hasOne relation, you need to do the following.
class Contract extends Model
{
public function web()
{
return $this->hasOne(Web::class)
->withDefault(function () {
return new Web();
});
}
}
As implemented like this its not possible to just return an empty array. Why this isn't possible, check out this issue on Laravel GitHub Issue Tracker.
There is existing code that depends on the result of any Eloquent relationship to either be null, a Model instance, or a Collection of Model instances. However, the current functionality of the withDefault() method opens up the potential for returning an object that is not one of those three expected values.
If you return a new \stdClass; or an empty array, an empty instance of web is returned. To get an empty array just instanciate a new Object of the relation class. In my case new Web();.

Your relationship method should be the one handeling this since it's the first place you can fix this
I checked this so it returns an array when the variable is null.
public class Contracts{
public function web(){
$collection = $this->hasMany('App\Web');
return $collection ? $collection : [];
}
}

Related

How to access attributes in laravel collection

I have this collection
Collection {#611
#items: array:1 [
0 => Payeur {#605
#guarded: array:1 [
0 => "id"
]
#attributes: array:11 [
"id" => 1
"prenom_payeur" => "aymen"
"nom_payeur" => "larabi"
"mobile_payeur" => "non"
"telephone" => 230493212
"observation" => "jeflolena"
"adresse" => "jdjfnrll"
"event_id" => 1
"adherent_id" => 3
"created_at" => "2019-12-22 14:57:43"
"updated_at" => "2019-12-22 14:57:43"
]
}
]
}
and I want to get the nom_payeur attributes from this collection. I did this many times before and I don't know why I get this error:
(Property [nom_payeur] does not exist on this collection instance.)
I'm trying to get the attributes by doing this: $payeur->nom_payeur , $payeur is the collection.
Please help.
try this hope its help
you have the collection and you try to access this property that is not possible beacuse in collection you can n't access element or attribute directly you have to use the array of index for this to access the poperty.
$payeur -> is collection this is incorrect way to access the property.
try this to access the attribute property.
$payeur[0]->nom_payeur

How can I select certain columns from my recursive child call

I'm pretty new to Eloquent and I'm having issues wrapping my head around something.
Basically I have a table which I'm recursively grabbing children from within the same table.
public function children() {
return $this->hasMany(static::class, 'parent_org_id');
}
public function childrenRec()
{
return $this->children()->with('childrenRec');
}
Where childrenRec is a recursive call to all children based on 'parent_org_id'
I'm calling it from the following in a static function, as of right now I only want the id and the name_en of the org
self::select('id','name_en')->where('parent_org_id','=',0)->with('childrenRec')->get()->toArray();
which is grabbing the top level org (my top level org has a parent_org_id of 0).
My issue is that in the recursively grabbed children it doesn't limit it to the id and the name_en
My question boils down to:
How can I select only certain columns from my recursive child calls, as well is this the 'proper' way of doing things?
My returned array looks like this.
array:1 [▼
0 => array:4 [▼
"id" => 1
"name_en" => "Org Unit"
"org_type" => null
"children_rec" => array:2 [▼
0 => array:27 [▼
"id" => 2
"name_en" => "My First Orgunit."
"code" => null
"abbreviation" => null
"address1" => "222 Street Street"
"address2" => null
"city_id" => 1
"province_id" => 14
"postalcode" => "C161L7"
"country_id" => 38
"contact_name" => null
"contact_title" => null
"email" => "test#test.com"
"fax" => "902-555-5555"
"phone1" => "5125125125125"
"phone2" => null
"org_type_id" => 1
"parent_org_id" => 1
"ref_id" => 79
"has_users" => 1
"created_at" => "2016-11-02 18:47:55"
"updated_at" => "2016-11-02 18:47:55"
"org_type" => array:4 [▶]
"children_rec" => array:1 [▶]
]
1 => array:27 [▶]
]
]
]
Thanks in advance.
To access the relation query in the with() method you use an array with the name of the relationship as the key and a closure with an instance of QueryBuilder injected.
One 'gotcha' that took me forever to track down a solution to when doing this is, your parent and children queries need to include the key that associates their relationship because the models are attached/associated with each other after both queries are run separately. Those models are associated with each other using the columns defined in the relation on the model. Without the columns used in the association of the models in your query, the related models won't be attached. In your situation it would be:
self::select('id','name_en')
->where('parent_org_id','=',0)
->with(['childrenRec' => function($query) {
return $query->select('id', 'name_en', 'parent_org_id');
}])
->get()
->toArray();
If you don't include the parent_org_id in the subquery the relationships won't get attached.
Docs
Try this:
public function childrenRec()
{
return $this->children()->with(['childrenRec' => function($query){
$query->select('id','name_en');
}]);
}

preg_match() expects parameter 2 to be string, array given Error

I'm trying to insert array but I'm getting error:-
preg_match() expects parameter 2 to be string, array given
My form below like :
{!! Form::text('description[]',null,['class' => 'input-field input-sm','v-model'=>'row.description']) !!}
{!! Form::text('log_time[]',null,['class' => 'input-field input-sm','v-model'=>'row.log_time']) !!}
My controller store function :
$this->validate($request, $this->rules);
$data = array();
foreach($request->description as $key=>$value){
$data[]=[
'description'=> $value,
'log_time'=> $request->log_time[$key],
'call_id'=>$call->id,
];
}
PortLog::create($data);
when i check dd($data)
array:2 [▼
0 => array:3 [▼
"description" => "des"
"log_time" => ""
"call_id" => 16
]
1 => array:3 [▼
"description" => ""
"log_time" => "hi"
"call_id" => 16
]
]
here what im doing wrong ?
It looks like you're attempting to insert multiple port_logs in one statement. However, the create() method is only meant to create one instance of a model. You either need to use the insert() statement, or update your code to foreach through your $data and issue multiple create() statements.
PortLog::insert($data);
// or
foreach($data as $row) {
PortLog::create($row);
}
If you just want to insert the data, and you don't want to instante a bunch of PortLog instances, then the insert() method is the way to go. If you need to instantiate a new PortLog instance for each row, then the create() method is the way to go.
check the Model fillable fields... it seems, that it missing []

How to insert relations based on a nested array in Laravel?

I have 3 tables tests, questions, options.
As you can imagine
a test has many questions
a question belongs to a test
a question has many options
an option belongs to a question
These relations are set up in the models already.
I got the data from the front end in this form:
array:3 [
"name" => "First Test"
"preparation" => "First Test prep"
"questions" => array:2 [
0 => array:2 [
"title" => "Some question"
"options" => array:4 [
0 => "a"
1 => "b"
2 => "c"
3 => "d"
]
]
1 => array:2 [
"title" => "Another question"
"options" => array:4 [
0 => "e"
1 => "f"
2 => "g"
3 => "h"
]
]
]
]
This data perfectly represents these relationships. In fact if I were using a NoSql database I would simply store this in the database.
My question is "what is the best way to store all of this data at once while using eloquent in Laravel"?
Note: It is in the form of Laravel's collection.
class Test extends Model {
$table = 'tests';
function questions(){
return $this->hasMany(Question::class, 'test_id');
}
}
class Question extends Model {
$table = 'questions';
function answers(){
return $this->hasMany(Answer::class, 'question_id');
}
function test(){
return $this->belongsTo(Test::class, 'test_id');
}
}
class Answer extends Model {
$table = 'answers';
function question(){
return $this->belongsTo(Question::class, 'question_id');
}
}
of course i showed only relations ,tables and foreign keys.
you can add any additional data to your models.
You have to create a structure of model objects from given data.
JMS Serializer is a great library to do that, and it has laravel integration.

Manually add item to existing object [Laravel 5]

Here is what I try to do:
$q = Question::where('id',$id -> id)->get();
$q[] = $q->push([ 'test' => true]);
dd($q);
This will output:
Collection {#220 ▼
#items: array:3 [▼
0 => Question {#225 ▶}
1 => array:1 [▼
"test" => true
]
2 => null
]
}
So 'test' => true will append as a new key, but I want to insert it in Question so latter I can access to it like this with foreach $q -> test
So here is how I want access to item:
#foreach($q as $qq)
{{ $qq->test }}
#endforeach
It can be done by using setAttribute() function of Eloquent Model (https://github.com/illuminate/database/blob/master/Eloquent/Model.php).
As You can see it stores data in protected $attributes using setAttribute(), and when we do $SomeModel->some_field it uses magic method __get() to retrieve item by association from attributes array.
Here is the resolution to Your question:
$Question = Question::find($id);
$Question->setAttribute('test', 'blablabla');
Apart from setAttribute(), you can use put() refer to this post for one item. And map() for many items, refer to this post.

Categories