I have a method that checks if a role is equal to 1 and then sends back some data. The method looks like so:
if($user->role == 1) {
$dmca = Dmca::get()->take(5);
$data = [
'status' => 200,
'data' => $dmca
];
return response($data);
} else {
$dmca = Dmca::where('client', $request->user_id)->get()->take(5);
$data = [
'status' => 200,
'data' => $dmca
];
return response($data);
}
}
On the dump and die of the $user instance, you can see the role is there and is set. But on return, I get the error
#attributes: array:11 [
"id" => 1
"name" => null
"email" => "Grahammorbydev#gmail.com"
"model_name" => "man"
"subscribed" => 0
"email_verified_at" => null
"password" => "$2y$10$yy1Yj.GGez7efEdFdkjaf.RlQS17Zc7QYUANz3RvdE00fVm0f9AYq"
"role" => 1
"remember_token" => null
"created_at" => "2020-07-05 17:54:38"
"updated_at" => "2020-07-05 17:54:38"
]
Getting the following error when the axios returns
"Property [role] does not exist on this collection instance."
As the error message is shown
"Property [role] does not exist on this collection instance."
It indicates that $user is an instance of Collection
You have to change
$user = User::where('id', $request->user_id)->get();
To this
$user = User::where('id', $request->user_id)->first();
get() : returns a collection of models matching the query.
first() : returns the first record found in the database. If no matching model exist, it returns null.
more info
Related
I do have a resource (DeviceResource) in my Laravel API which contains another resource (SimcardResource). There is a OnetoOne relationship between those resources but sometimes a device has no associated simcard.
If this is the case my DeviceResource returns for the simcard null instead of an empty json object.
I do need an empty json object because I present information called from my API in my Vue frontend by accessing an object e.g. like device.simcard.phone_number
My DeviceResource class looks like this:
public function toArray($request)
{
return [
'id' => $this->resource->id,
'model' => $this->resource->model,
'device_name' => $this->resource->device_name,
'operating_system' => $this->resource->operating_system,
'os_version' => $this->resource->os_version,
'emei' => $this->resource->emei,
'device_password' => $this->resource->device_password,
'purchase_date' => $this->resource->purchase_date,
'associated_worker' => $this->resource->associated_worker,
'remarks' => $this->resource->remarks,
'device_status_id' => $this->resource->device_status_id,
// 'simcard' => $this->resource->simcard ?: (object)[],
'simcard' => SimcardResource::make($this->whenLoaded('simcard')) ?: (object)[],
];
}
The commented section:
'simcard' => $this->resource->simcard ?: (object)[]
Works perfectly but returns all fields from my simcard table but I only need fields defined in my SimcardResource class so I tried the following:
'simcard' => SimcardResource::make($this->whenLoaded('simcard')) ?: (object)[]
But it still returns null instead of an empty json object.
Okay maybe its not the best solution but my DeviceResource class now looks like this:
public function toArray($request)
{
if (is_null($this->resource->simcard)) {
return [
'id' => $this->resource->id,
'model' => $this->resource->model,
'device_name' => $this->resource->device_name,
'operating_system' => $this->resource->operating_system,
'os_version' => $this->resource->os_version,
'emei' => $this->resource->emei,
'device_password' => $this->resource->device_password,
'purchase_date' => $this->resource->purchase_date,
'associated_worker' => $this->resource->associated_worker,
'remarks' => $this->resource->remarks,
'device_status_id' => $this->resource->device_status_id,
'simcard' => (object) [],
];
} else {
return [
'id' => $this->resource->id,
'model' => $this->resource->model,
'device_name' => $this->resource->device_name,
'operating_system' => $this->resource->operating_system,
'os_version' => $this->resource->os_version,
'emei' => $this->resource->emei,
'device_password' => $this->resource->device_password,
'purchase_date' => $this->resource->purchase_date,
'associated_worker' => $this->resource->associated_worker,
'remarks' => $this->resource->remarks,
'device_status_id' => $this->resource->device_status_id,
// 'simcard' => $this->resource->simcard ?: (object)[],
'simcard' => SimcardResource::make($this->whenLoaded('simcard')),
];
}
}
It is Laravel resource default behaviour that if you do not have any data than resource will also return you the null resource object. You have to manage it yourself in other way like by defining each parameter has null value that's it.
Laravel introduce best ways,for example whenLoaded,but try this
... ?? json_encode(new stdClass)
I have an array, which looks like this:
array:3 [▼
"field" => array:2 [▼
0 => "fromName"
1 => "from"
]
"operator" => array:2 [▼
0 => "="
1 => "="
]
"value" => array:2 [▼
0 => "Oliver"
1 => "oliver#mywebsite.com"
]
]
I am trying to save the above array, into my database table called email_rules:
Below is my code.
StreamEmailRulesController.php:
public function store(Stream $stream)
{
//Validate the request.
//Validate the request.
$attributes = request()->validate([
'field' => 'required|array|min:1',
'field.*' => [
'required', 'string',
Rule::in(['fromName', 'from']),
],
'operator' => 'required|array|min:1',
'operator.*' => [
'required', 'string',
Rule::in(['=', '!=', 'matches']),
],
'value' => 'required|array|min:1',
'value.*' => 'required|string|min:3|max:255',
]);
//Add the document to the database.
$stream->addRules($attributes);
//Return back.
return redirect()->back();
}
Now the $stream->addRules() function is responsible for saving the data to the database.
Stream.php:
/**
* A stream can have many rules.
*/
public function rules()
{
return $this->hasMany(EmailRule::class);
}
/**
* Add Email Rules(s) to the stream
*
* #return Illuminate\Database\Eloquent\Model
*/
public function addRules(array $attributes)
{
return $this->rules()->create($attributes);
}
Now, above does not work. I get below error:
Argument 1 passed to Illuminate\Database\Grammar::parameterize() must be of the type array, int given,
What am I doing wrong?
If you dump $attributes you may be getting an int (bool) as a pass or fail or even json, depending on what's going in, from the validation. This might just be a matter of changing syntax from
$attributes = request()->validate([...
to
$attributes= $this->validate(request(), [...
I believe your issue is that you're trying to save an array as a singular value. IE those attributes need to be iterated over to create a new set of rules for each one, instead. Normally, I'd expect to see the array ready to create individual objects. In this case, though it looks like it is structured to create individual fields (field, operator, value), so looping through those may not do what you wish either -- it provides multiple fields to the create construct, rather than a full set of object params for a new rule(). I think Laravel is hinting that you may wish to change your request/return structure to match the model format.
I think it could be the array structure. Can you modify the array to?:
[
[
"field" => "fromName",
"operator" => "=",
"value" => "Oliver"
],
[
"field" => "from",
"operator" => "=",
"value" => "oliver#mywebsite.com"
],
]
EDIT:
In the Controller add a loop like this:
...
foreach ($attributes as $key => $value) {
foreach ($value as $k => $v) {
$data [$k][$key] = $v;
}
}
//Add the document to the database.
$stream->addRules($data);
The problem was that Laravels create or createMany expect an array with key => pair values, where the key corresponds to the database columns.
This article from Adam Wathan helped me out a lot.
This is what I ended up doing:
$requestData = collect(request()->only('field', 'operator', 'value'));
$rules = $requestData->transpose()->map(function ($ruleData) {
return new EmailRule([
'field' => $ruleData[0],
'operator' => $ruleData[1],
'value' => $ruleData[2],
]);
})->toArray();
//Add the rules to the database.
$stream->addRules($rules);
I have one to many relationship between users and posts.
I want to know if I eager load in the posts can I modify the collection without a new attribute getting created on the User model.
$user = User::with('posts')->get();
// Filter the posts on the user using business logic.
// Below is an example. I DO NOT want to do this logic in the db/query builder.
// Arbitrary business rule that is not easily possible to calculate in DB
$shouldGetTestPost = true;
$user->posts = $user->posts->filter(function($post) use ($shouldGetTestPost) {
if ($shouldGetTestPost && $post->name = 'test') {
return true;
}
return false;
});
dd($user);
If I run the above code laravel will create a new attribute called posts and assign the collection to that attribute instead of modifying the relationship.
e.g.
// I've removed irrelevant model data
App\User {#1145
#table: "users"
#attributes: array:6 [
"id" => 1
"email" => "test#test.com"
"password" => "secret"
"updated_at" => "2019-02-11 18:56:35"
"created_at" => "2019-02-11 18:56:35"
// Below is new
"posts" => Illuminate\Database\Eloquent\Collection {#1217
#items: array:1 [
0 => App\Post {#1269
#table: "posts"
#attributes: array:24 [
"id" => 1
"name" => 'test'
"user_id" => 1
]
}
]
}
]
#original: array:5 [...]
#relations: array:1 [
"posts" => Illuminate\Database\Eloquent\Collection {#1264
#items: array:2 [
0 => App\Post {#1269}
1 => App\Post {#1234
#table: "posts"
#attributes: array:24 [
"id" => 1
"name" => 'test'
"user_id" => 1
]
}
]
}
]
]
Another interesting thing that happens is that in #relations the data is removed from theitems array but the reference still remains. See 0 => App\Post {#1269}
Is this intended for laravel? I'm aware that laravel prioritises attributes over relations but it seems odd that it wouldn't change the relations array.
How would I only change the relations collection and not create a new attribute?
The setRelation method can be used to override what's stored in a relation.
$model->setRelation($relation, $value)
https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_setRelation
However, there's very few situations where this would be useful so I still believe this is an XY problem. You can constrain the eager loading query by passing a closure as the second argument to with() which would be the solution to conditionally retrieving related items.
I've got the following validation rules for basic authentication of a Payment Method (advanced things, like CVD validation, existing card, etc. is handled afterward by Moneris).
$rules = [
"type" => "required|in:visa,mastercard",
"nickname" => "required",
"credit_card_number" => "required|numeric|digits:16",
"expiry" => "required|string|size:5|date_format:m/y|after:today",
"cvd" => "required|numeric|digits:3"
];
The rule expiry is not accepting a specific value, 04/yy, but it is accepting 03/yy and 05/yy; I have no idea why this is happening, but I need it remedied. Has anyone come across this behaviour?
For reference, the result dd($request->input(), $validator->passes(), $validator->errors()); when I pass 04/19 is as follows:
array:6 [▼
"type" => "visa"
"nickname" => "Testing"
"credit_card_number" => "4242424242424242"
"expiry" => "04/19"
"cvd" => "123"
"masked_pan" => "************4242"
]
false
MessageBag {#502 ▼
#messages: array:1 [▼
"expiry" => array:1 [▼
0 => "The expiry does not match the format m/y."
]
]
#format: ":message"
}
When I send 05/19, everything works fine:
array:6 [▼
"type" => "visa"
"nickname" => "Testing"
"credit_card_number" => "4242424242424242"
"expiry" => "05/19"
"cvd" => "123"
"masked_pan" => "************4242"
]
true
MessageBag {#502 ▼
#messages: []
#format: ":message"
}
Looks like it's an issue with how this validation rule works in Laravel 5.4. To fix, I check the date validity of the input prepended with 01/, and if it is valid, merge that into the request, with endOfMonth() to handle after:today validation:
$mergeDate = null;
$rawInput = $request->input("expiry");
try {
$mergeDate = Carbon::createFromFormat("d/m/y", "01/".$request->input("expiry"))->endOfMonth();
} catch(\Exception $ex){}
$request->merge([
"masked_pan" => str_repeat("*", 12).substr($request->input("credit_card_number", ""), -4),
"expiry" => $mergeDate ? $mergeDate->format("d/m/y") : $request->input("expiry")
]);
So now, if I pass 04/22, it will check if 01/04/22 is valid, then convert to end of month 30/04/22, then replace that as the value passed to the validation (which also needs to be updated)
"expiry" => "required|string|size:8|date_format:d/m/y|after:today",
I also have to update and pass $messages to avoid confusion to the user:
$messages = [
"expiry.size" => "The :attribute filed must be 5 characters.",
"expiry.date_format" => "The :attribute field does not match the format m/y"
];
$validator = \Validator::make($request->all(), $rules, $messages);
And finally, replace the value with the raw input if there's an error (so the user doesn't see a value they didn't enter)
if(!$validator->passes()){
$request->merge(["expiry" => $rawInput]);
return back()->withErrors($validator)->withInput();
}
A whole bunch of nonsense, but seems to handle 04/22 and other dates just fine.
I have a search engine with typeahead. What I want is that after doing a search and giving the submit, show the results. This gives two problems: first it returns an empty array and second, it does not allow me to access the properties telling me it is not an object.
In controller, I used collect() to allow me to access the properties, but it does not work and WHERE either.
public function store(Request $request)
{
$url = 'storage/json/es/noticia.json';
$datos = file_get_contents($url);
$data = json_decode($datos, true);
$data = collect($data)->where("title","LIKE","%{$request->texto}%")->all();
return view('web.buscar.index', compact('data'));
}
If I use $data = collect($data)->all(); I can see the collection:
array:8 [▼
0 => []
1 => array:4 [▼
"id" => 2
"title" => "There is a title"
"lead" => "There is a lead"
"slug" => "there-is-a-title"
]
2 => array:4 [▶]
3 => array:4 [▶]
4 => array:4 [▶]
5 => array:4 [▶]
6 => array:4 [▶]
7 => array:4 [▶]
]
Then If I try: $value->title in the view I have the error: Trying to get property 'title' of non-object. In the view I have:
{!! Form::open([
'route' => 'buscar',
'id' => 'buscar',
'name' => 'buscar',
'class' => 'buscador col-xs-12',
'method' => 'POST',
'accept-charset' => 'utf-8'
]) !!}
<input id="texto" name="texto" class="input_buscador typetitulo" autocomplete="off" type="text"/>
{!! HTML::image('images/web/icons/lupa.svg', '', array('height' => '30', 'class' => 'boton_buscador', 'onclick' => 'document.buscar.submit()') ) !!}
{!! Form::close() !!}
#if(isset($data))
#foreach($data as $value)
<span>{{$value->title}}</span><br>
#endforeach
#endif
If I use $data = collect($data)->pluck('title'); in the controller and in the view I don't call the property 'title', this works, but it's not what I want because I need to access other properties too.
Any idea? Thanks in advance!
This is failing because the first array in your array does not have any values, so you will get undefined index, remove any empty arrays by doing
public function store(Request $request)
{
$url = 'storage/json/es/noticia.json';
$datos = file_get_contents($url);
$data = json_decode($datos, true);
$data = array_filter($data);
$data = collect($data)->where("title","LIKE","%{$request->texto}%")->all();
return view('web.buscar.index', compact('data'));
}
Or you can test to see if it is there in your foreach
#foreach($data as $value)
<span>{{$value->title ?? ''}}</span><br>
#endforeach
You can then search the collection using a filter
collect($data)->filter(function ($item) use ($request) {
return $item->title == $request->texto;
)
You can edit the return to be more granular using stristr etc