DBIx::Class (Perl ORM) equivalent in PHP - php

We started our web-based app back in 2012 when Perl was still popular. Now we want to reimplement it in PHP, preferably Laravel. However, I miss the power of DBIx::Class in their default ORM, Eloquent, and the alternative, Doctrine, from reading the docs seems more complicated, but doesn't solve the issue either. What I use DBIx::Class for:
Regenerate model classes from reading the DB structure, preserving any code that has been added to them (should have).
Generate complex queries (incl. nested joins, subqueries) from pure language constructs, no SQL needed (must have).
Code sample:
rows => $rows,
page => $page,
'select' => [
'entity_id',
{ group_concat => [ { distinct => 'pictures.id' } ], -as => 'photos' },
{ '' => [ 'entity.name' ], -as => 'entity_name' },
{ '' => [ 'municipality.name' ], -as => 'municipality_name' },
{ '' => [ 'me.birth_date' ], -as => 'bd' },
{ concat => [ { date_format => [ 'me.birth_date', \$c->config->{dateFormat} ] }, \"' ('", { timestampdiff => [ \'YEAR', 'me.birth_date', \'CURDATE()' ] }, \"')'" ], -as
{ date_format => [ 'me.birth_date', \$c->config->{dateFormat} ], -as => 'bd1' },
{ timestampdiff => [ \'YEAR', 'me.birth_date', \'CURDATE()' ], -as => 'bd2' },
{ '' => [ 'entity_state_type.name' ], -as => 'entity_state_name' },
{ '' => [ 'data_list_item.name' ], -as => 'entity_state_reason_name' },
{ '' => [ 'entity_state.note_text' ], -as => 'entity_state_note' },
{ '' => [ { if => [ 'entity.archived', \"'Yes'", \"''" ] } ], -as => 'entity_archived' },
],
join => {
entity => [ 'municipality', 'pictures', { 'entity_state' => [ 'entity_state_type', 'data_list_item' ] } ],
},
group_by => [ 'entity.id' ],
Subclass the result (row) and resultset classes to add user role-based limiting conditions transparently to the application code (must have, or equivalent).
The call and the resulting query:
$c->model('DB::Person')->all
SELECT * FROM person WHERE owner = <user_id>
...if the user role security settings indicate user should only access his own persons.
The security settings are read from DB on request start and specify which security conditions (separate classes) should apply to which table.
This moves all security away from application code, where mistakes are made most often.
Which PHP library would allow us to implement this?

Related

How to add "runtime_mappings" to the query for FOS/Elastica in PHP?

Need to add the next json query to php code with using FOSElasticaBundle:
"runtime_mappings": {
"Agreement": {
"type": "keyword",
"script": {
"source": "if(doc['winningBidder.edrpou'].size()>0 && doc['seller'].size()>0)\r\n{\r\nemit(\r\n doc['seller'].value+\":\"+\r\n doc['trading.id'].value+\":\"+\r\n doc['winningBidder.edrpou'].value+\":\"\r\n )\r\n}"
}
}
}
If I'm set this in simple method (\Elastica\Query)->addParam():
->addParam('runtime_mappings', [
'Agreement' => [
'type' => 'keyword',
'script' => [
'source' => "if(doc['winningBidder.edrpou'].size()>0 && doc['seller'].size()>0)\r\n{\r\nemit(\r\n doc['seller'].value+\":\"+\r\n doc['trading.id'].value+\":\"+\r\n doc['winningBidder.edrpou'].value+\":\"\r\n )\r\n}"
]
]
])
Then I get an error when I try to collect the query:
Unknown key for a START_ARRAY in [runtime_mappings].
Alternatively, you can create a query by starting with the "runtime_mappings" array and adding all other parameters after:
$query = \Elastica\Query::create(['runtime_mappings' => [
'Agreement' => [
'type' => 'keyword',
'script' => [
'source' => "if(doc['winningBidder.edrpou'].size()>0 && doc['seller'].size()>0)\r\n{\r\nemit(\r\n doc['seller'].value+\":\"+\r\n doc['trading.id'].value+\":\"+\r\n doc['winningBidder.edrpou'].value+\":\"\r\n )\r\n}"
]
],
]]);
// add other params to query...
$query->addParam();

PHP: array_map to return specific array of objects

I have a codepen here which shows a large array of objects. From which I would like to extract a specific property and display as shown in the console log, but in PHP.
Unfortunately for me, I'm quite new to PHP and can't seem to figure it out:
My attempt so far:
$suppliersNotInDB = array_map(function ($order) {
if (isset($order->items) && is_array($order->items)) {
return array_map(function ($item) {
return [...$item];
}, $order->items);
}
}, $ordersData);
Which, I understand isn't even remotely close, but I've been at it for hours now. Any help would be appreciated.
The long and short is that I want to perform this filtering and sorting in the backend(Laravel), not the frontend, where there is already too much going on.
Since you are using Laravel, start using Collections.
If I understood correctly what you are trying to do in your Javascript example, this can be done in a one-liner:
$suppliersNotInDB = collect($ordersData)
->pluck('items')
->flatten()
->pluck('supplier')
->unique()
->map(
fn($supplier) => ['name' => $supplier, 'lowercased' => strtolower($supplier)]
)
->values();
This can probably be further refined, just quickly jotted it down to reproduce your result.
The output of this would then be:
=> Illuminate\Support\Collection {#4999
all: [
[
"name" => "Walmart",
"lowercased" => "walmart",
],
[
"name" => "Bestbuy",
"lowercased" => "bestbuy",
],
[
"name" => "TCI",
"lowercased" => "tci",
],
[
"name" => "lkj",
"lowercased" => "lkj",
],
[
"name" => "Thousand Needles",
"lowercased" => "thousand needles",
],
],
}

ElasticSearch 7 & PHP - Create mapping for parent / child relation

I want to index in my index buildings 2 types of documents: building & apartment.
A building can have several apartment, so I want to use parent/child relationships.
I use ES 7.10 and PHP with official PHP lib (elasticsearch/elasticsearch).
According to the doc (https://www.elastic.co/guide/en/elasticsearch/guide/current/parent-child-mapping.html), I have to use something like :
PUT index buildings
{
"mappings": {
"building": {},
"apartment": {
"_parent": {
"type": "building"
}
}
}
}
In my PHP I have :
$this->getClient()->indices()
->create([
'index' => 'buildings',
'body' => [
'mappings' => [
'building' => [],
'apartment' => [
'_parent' => [
'type' => 'building'
]
]
]
]
]);
Which throws the error:
Root mapping definition has unsupported parameters: [apartment: {_parent={type=building}}] [building: []]"}},"status":400}
Did I miss something?
Parent/type has been deprecated in favor of join type in Elasticsearch 7 as far as I remember, so this might be the reason you're getting the exception.
Link to docs: https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html

Craft Element API data output as a hash instead of an array

I'm building and API feed using Element API plugin from Craft, and I'd like the data output to be serialized as a hash, but currently it's returning an array, as example bellow:
<?php
namespace Craft;
return [
'endpoints' => [
'event-name/feed.json' => [
'elementType' => ElementType::Entry,
'criteria' => ['section' => 'event1'],
'transformer' => function(EntryModel $entry) {
$speakersList = [];
foreach ($entry->speakersList as $speaker) {
$speakersList[] = [
'name' => $speaker->speakerFullName,
'jobTitle' => $speaker->speakerJobTitle,
'company' => $speaker->speakerCompany,
];
}
return [
'speakers' => $speakersList,
];
},
],
];
And the output:
{
"data": [
{
"speakers": [
{
"name": "John Doe",
"jobTitle": "Marketing",
"company": "Company 1",
},
...
I've tried the serialize options in the documentation, but none seemed to solve the issue.
Currently if I'd like to access the speakers within data I'd have to first access index[0] to be able to get to the speakers key.
Is there a way to get rid of this array level?

Is it possible to only use filters without text search in elasticsearch

I am using ES for my Laravel app, and I need to do a search query that only contains filters and no "text search" but I am not sure on how to write it.
Must I use match_all eg:
$query = [
'filtered' => [
'query' => [
'match_all' => []
],
'filter'=> [
'bool' => [
'must' => [
[ 'range' => [
'price' => [
'lte' => 9000
]
]
],
],
]
],
],
];
Or like this:
$query = [
'filtered' => [
'filter'=> [
'bool' => [
'must' => [
[ 'range' => [
'price' => [
'lte' => 9000
]
]
],
],
]
],
],
];
What I want is to only use a filtered bool query without text search.
In fact, if you don't specify the query part in your filtered query, a match_all query is used by default. Quoting the doc :
If a query is not specified, it defaults to the match_all query. This
means that the filtered query can be used to wrap just a filter, so
that it can be used wherever a query is expected.
Your second query should do the job : filters must be wrapped either in filtered (doc) or constant_score (doc) queries to be used.
If the scoring part isn't useful for you, you can stick to the filtered query.
Last thing : you don't have to nest your filter in a bool filter, unless you want to combine it with other(s) filter(s). In your demo case, you can write directly :
$query = [
'filtered' => [
'filter'=> [
'range' => [
'price' => [
'lte' => 9000
]
]
]
]
];
Hope this will be helpful :)
It's actually exactly the same thing since if a query is not specified in the clause it defaults to using the match_all query.
While in query context, if you need to use a filter without a query (for instance, to match all emails in the inbox), you can just omit the query:
GET /_search
{
"query": {
"filtered": {
"filter": { "term": { "folder": "inbox" }}
}
}
}
If a query is not specified it defaults to using the match_all query, so the preceding query is equivalent to the following:
GET /_search
{
"query": {
"filtered": {
"query": { "match_all": {}},
"filter": { "term": { "folder": "inbox" }}
}
}
}
Check here the official documentation: http://www.elastic.co/guide/en/elasticsearch/guide/current/_combining_queries_with_filters.html

Categories