Laravel Valiadtion array must have one element with a certain value - laravel

Imaging having the following input for a form request validation.
[
'relations' =>
[
[
'primary' => true,
],
[
'primary' => false,
],
],
],
Is there any validation that makes it possible to secure that at least one of the relations models have primary set to true? More perfect if it could secure only one element is true. This problem seems like it could have existed before.
So if we only see the input for relations, this should pass.
[
'primary' => true,
],
[
'primary' => false,
],
This should fail in validation.
[
'primary' => false,
],
[
'primary' => false,
],

Try an inline custom rule:
public function rules()
{
return [
'relations' => function ($attribute, $relations, $fail) {
$hasPrimary = collect($relations)
->filter(function ($el) {
return $el['primary'];
})
->isNotEmpty();
if ( ! $hasPrimary)
{
$fail($attribute . ' need to have at least one element set as primary.');
}
},
// the rest of your validation rules..
];
}
Of course, you could extract this to a dedicated Rule object, but you get the idea.

Related

Problem with Collection::sortBy() in cakephp 4

I'm trying to sort a list of email accounts by alphabetical order using Collection::sortBy() in cakephp 4, but it seems not working the way I use it.
$accounts = [
[
'email' => 'webmaster#example.com',
'isBlocked' => false,
],
[
'email' => 'dom#example.com',
'isBlocked' => false,
],
[
'email' => 'me#example.com',
'isBlocked' => false,
],
[
'email' => 'guy#example.com',
'isBlocked' => false,
],
[
'email' => 'test#example.com',
'isBlocked' => false,
]
];
$sorted = collection($accounts)
->sortBy('email', SORT_ASC)
->toArray();
debug($sorted);
debug($sorted) returns exactly the same array as $accounts...
What am I doing wrong ?
The default sort type is SORT_NUMERIC, and converted to numbers, all your strings will be 0, hence all are equal as seen by the sorting mechanism, and nothing will chance.
For strings use SORT_NATURAL, SORT_STRING, SORT_LOCALE_STRING, or SORT_REGULAR, eg:
sortBy('email', SORT_ASC, SORT_NATURAL)
The Cookbook needs a fix there I think, as it shows referencing string fields without specifying the required sort type.
See also
PHP Manual > Function Reference > Variable and Type Related Extensions > Arrays > Array Functions > sort
Cookbook > Collections > Sorting

Laravel Elasticsearch JSON Mapping Issue

I'm currently using Laravel v7.2, have the babenkoivan/scout-elasticsearch-driver installed (4.2) and am using AWS Elasticsearch 7.1. I have several tables mapped in my application that are working fine but am having issues with a nested mapping that was previously working and is now broken.
I'm saving data into a table and having that table data copied into AWS Elasticsearch. I'm using MySQL 5.6 so I am using a TEXT column to store JSON data. Data in the table looks as follows:
'id' => 1,
'results' => [{"finish":1,"other_id":1,"other_data":1}]
I have my model setup with the following mapping:
protected $mapping = [
'properties' => [
'results' => [
'type' => 'nested',
'properties' => [
'finish' => [
'type' => 'integer'
],
'other_id' => [
'type' => 'integer'
],
'other_data' => [
'type' => 'integer'
]
]
],
]
];
And if it's of any use, the toSearchableArray:
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'results' => $this->results
];
return $array;
}
I have no problem creating this index and it worked up until about a couple of months ago. I don't know exactly when, as it wasn't a high priority item and may have occurred around an AWS ES update but not sure why this in particular would break. I receive the following error now:
{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"object mapping for [results] tried to parse field [results] as object, but found a concrete value"}],"type":"mapper_parsing_exception","reason":"object mapping for [results] tried to parse field [results] as object, but found a concrete value"},"status":400}
I've tried also storing the data in the table as such, thinking it was breaking due to the potential array, but it was to no avail:
'id' => 1,
'results' => {"finish":1,"other_id":1,"other_data":1}
I'm at a loss for what else to try to get this working again.
EDIT: Here is the entire model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use ScoutElastic\Searchable;
class ResultsModel extends Model
{
use Searchable;
protected $indexConfigurator = \App\MyIndexConfiguratorResults::class;
protected $searchRules = [
//
];
protected $mapping = [
'properties' => [
'results' => [
'type' => 'nested',
'properties' => [
'finish' => [
'type' => 'integer'
],
'other_id' => [
'type' => 'integer'
],
'other_data' => [
'type' => 'integer'
]
]
],
]
];
public function searchableAs()
{
return 'results_index';
}
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'results' => $this->results
];
return $array;
}
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'results_table';
}
Here is the \App\MyIndexConfiguratorResults::class
<?php
namespace App;
use ScoutElastic\IndexConfigurator;
use ScoutElastic\Migratable;
class MyIndexConfiguratorResults extends IndexConfigurator
{
use Migratable;
protected $name = "results_index";
/**
* #var array
*/
protected $settings = [
//
];
}
This is all that is needed to have Laravel automatically update AWS ES each time the table is updated. For the initial load, I would SSH in and run the following command to have it create the index. This, as well as elastic:migrate and any update/insert into the table produces the mapping error.
php artisan elastic:create-index results_index
Finally figured this out so will share the solution for anyone that runs into this. Turns out to be a fairly simple fix, though I'm not sure how it even worked in the first place so that part is still baffling.
I created a brand new index and update the mappings accordingly to add "id" and remove the type "nested" from the "results" piece. (Adding the "nested" type was adding two "results" to the index - one that contained all my nested data, the other just being "object".)
protected $mapping = [
'properties' => [
'id' => [
'type' => 'integer'
],
'results' => [
'properties' => [
'finish' => [
'type' => 'integer'
],
'other_id' => [
'type' => 'integer'
],
'other_data' => [
'type' => 'integer'
]
]
],
]
];
Then I simply added json_decode to the toSearchableArray() function as so:
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'results' => json_decode($this->results, true)
];
return $array;
}
Voila. It successfully created the index and imported the data in a manner with which I can query the nested object.
Reading through the docs, the type field seems to have been removed. Scroll down to 7.x to see. Also, it seems you need to delete the index and re-add it in order for the new map to work according to this page.

How can I assert an instance of a ResourceCollection in Laravel?

I am working with a feature test and it's returning data correctly; all things are coming back correctly; and I'm at the final portion of my test.
I am struggling to assert that I'm getting back a ResourceCollection:
$this->assertInstanceOf(ResourceCollection::class, $response);
Here is the portion of my test:
MyFeature.php
...
$http->assertStatus(200)
->assertJsonStructure([
'data' => [
'*' => [
'type', 'id', 'attributes' => [
'foo', 'bar', 'baz',
],
],
],
'links' => [
'first', 'last', 'prev', 'next',
],
'meta' => [
'current_page', 'from', 'last_page', 'path', 'per_page', 'to', 'total',
],
]);
// Everything is great up to this point...
$this->assertInstanceOf(ResourceCollection::class, $response);
The error I get back is:
Failed asserting that stdClass Object (...) is an instance of class "Illuminate\Http\Resources\Json\ResourceCollection".
I'm not sure what I should be asserting in this case. I am getting back a resource collection, what should I be using instead? Thank you for any suggestions!
EDIT
Thank you #mare96! Your suggestion lead me to another approach that seemed to work. Which is great but I'm not too sure I really understand why...
Here's my full test (including my final assertion):
public function mytest() {
$user = factory(User::class)->create();
$foo = factory(Foo::class)->create();
$http = $this->actingAs($user, 'api')
->postJson('api/v1/foo', $foo);
$http->assertStatus(200)
->assertJsonStructure([
'data' => [
'*' => [
'type', 'id', 'attributes' => [
'foo', 'bar', 'baz'
],
],
],
'links' => [
'first', 'last', 'prev', 'next',
],
'meta' => [
'current_page', 'from', 'last_page', 'path', 'per_page', 'to', 'total',
],
]);
$this->assertInstanceOf(Collection::class, $http->getOriginalContent());
}
As I said in the comment above, your content will be the instance of Collection.
You can do it like that:
$this->assertInstanceOf(Collection::class, $http->getOriginalContent());
So, you can try to debug, to make it clearer, like this: Do dd($http); you should get an instance of Illuminate\Foundation\Testing\TestResponse not the same when you do $http->dump(); right?
So you need to assert an instance of just content, not the whole response.
I hope at least I helped a little.

Incomplete links generated in hal (zend-expressive-hal + zend-router)

My routes def:
'router' => [
'routes' => [
'TimeTable' => [
'type' => 'Literal',
'options' => [
// Change this to something specific to your module
'route' => '/tt',
],
'may_terminate' => false,
'child_routes' => [
'API' => [
'type' => 'Literal',
'options' => [
// Change this to something specific to your module
'route' => '/api',
],
'may_terminate' => false,
'child_routes' => [
'lines' => [
'type' => 'Literal',
'options' => [
// Change this to something specific to your module
'route' => '/lines',
'defaults' => [
'controller' => Controller\LineRestApiController::class,
],
],
'may_terminate' => true,
],
],
],
],
],
],
Medatada map def:
MetadataMap::class => [
[
'__class__' => RouteBasedCollectionMetadata::class,
'collection_class' => LineCollection::class,
'collection_relation' => 'lines',
'route' => 'TimeTable/API/Lines',
],
]
Generated result:
{
"_total_items": 78,
"_page": 1,
"_page_count": 4,
"_links": {
"self": {
"href": "http://xxx.xxx.xx"
},
"next": {
"href": "http://xxx.xxx.xx"
},
"last": {
"href": "http://xxx.xxx.xx"
}
},
"_embedded": {
"lines": [.....] }}
All links are generated with incomplete href , there is only domain part, route part is stripped ..
Expected result is something like this:
"href" : "http://xxx.xxx.xx/xxx/tt/api/lines....."
Im doing something wrong, I have no idea where to start ..
Thanks everyone for giving me some ideas
Simplified Controller code:
$psr7request = Psr7ServerRequest::fromZend($this->getRequest());
$list = this->entityManager->getRepository(Line::class)->getValidLinesCollection();
$resource = $this->resourceGenerator->fromObject($list, $psr7request);
echo Psr7Response::toZend($this->responseFactory->createResponse($psr7request, $resource))->getBody();
exit;
PS: Im not using full zend-expressive just zend-framework..
Sorry i forgot that i had done in last week :(
It must be done some custom implementation for UrlGeneratorInterface to successful integrate zend-expressive-hal to zend framework (original class ExpressiveUrlGenerator uses Expressive\Helper\ServerUrlHelper & UrlHelper, the part of Expressive)
So i used Zend\View\Helper\ServerUrl & Url to done it.
I have small typo in code. The final class is here:
use Psr\Http\Message\ServerRequestInterface;
use Zend\Expressive\Hal\LinkGenerator\UrlGeneratorInterface;
use Zend\View\Helper\ServerUrl as ServerUrlHelper;
use Zend\View\Helper\Url as UrlHelper;
class HalUrlGenerator implements UrlGeneratorInterface
{
/**
* #var null|ServerUrlHelper
*/
private $serverUrlHelper;
/**
* #var UrlHelper
*/
private $urlHelper;
public function __construct(UrlHelper $urlHelper, ServerUrlHelper $serverUrlHelper = null)
{
$this->urlHelper = $urlHelper;
$this->serverUrlHelper = $serverUrlHelper;
}
public function generate(
ServerRequestInterface $request,
string $routeName,
array $routeParams = [],
array $queryParams = []
) : string {
$urlHelper = $this->urlHelper;
$path = $urlHelper($routeName, $routeParams, ['query'=> $queryParams]);
if (! $this->serverUrlHelper) {
return $path;
}
$serverUrlHelper = $this->serverUrlHelper;
return $serverUrlHelper($path);
}
}
I hope the code can help somebody.

Why the validation message are not appearing with required_if?

I want that the fields payment_mb, payment_c and invoice_issuer are mandatory if the user introduces a value greater than "0" for field registration_type_price.
But its not working, the validation message is not appearing with "required_if:registration_type_price,>,0'". Do you know why?
$rules = [
...
'payment_mb' => 'required_without:payment_c|required_if:registration_type_price,>,0',
'payment_c' => 'required_without:payment_mb|required_if:registration_type_price,>,0',
'invoice_issuer' => 'required_if:registration_type_price,>,0',
];
$customMessages = [
...
'payment_mb.required_if' => 'The field payment methods is mandatory.',
'payment_c.required_if' => 'The field payment methods is mandatory.',
'invoice_issuer.required_if' => 'The field invoice issuer is mandatory..'
];
$this->validate($request, $rules, $customMessages);
try this one
$("#personal_detail_form").validate({
rules: {
full_name: {
lettersonly: true
},phone_number : {
number : true,
minlength:10,
maxlength:10
},city : {
lettersonly : true
},pin_code : {
digits : true,
minlength:6,
maxlength:6
},

Resources