Modify Solr Query by adding additional filters in TYPO3 - typo3

I'm trying to add custom filters to the query (TYPO3 v10, EXT:solr 11.2). However, this doesn't want to work.
After I simplified the use-case significantly and debugged it, I'm still not further, but rather more confused.
Filter works, if added via TypoScript:
plugin.tx_solr {
search {
filter {
jobTitle = title:Dev*
}
}
}
The same filter added via modifySearchQuery-hook does not work:
public function modifyQuery(Query $query)
{
$filterQuery = new FilterQuery([
'key' => 'jobTitle2',
'value' => 'title:Dev*',
]);
return $query->addFilterQuery($filterQuery);
}
When debugging the query, both filters look the same.

Thanks to Guido, who hit me on the right point: sometimes, keys are keys.
In the hook, the array-keys for FilterQuery have to be key and query (not value, as I've used)
public function modifyQuery(Query $query)
{
$filterQuery = new FilterQuery([
'key' => 'jobTitle2',
'query' => 'title:Dev*', // <-- correct key
]);
return $query->addFilterQuery($filterQuery);
}

Related

Why is double underscore needed here for accessing table field?

I came across a GitHub issue about sorting rows with TypeORM. I found this comment did work for my problem.
Quote:
async sortWithRelations(entityRepository) {
// Assuming repository classname is RepoX
let repoOptions = {
relations: ['relationA', 'relationB'],
where: qb => {
// Filter if required
qb.where('RepoX__relationA.fieldY = :val', {val: 'searchedValue'});
// Then sort
qb.orderBy({
'RepoX__relationA.fieldYYY': 'DESC',
'RepoX__relationB.fieldZZZ': 'DESC'
// '{repositoryClassName}__{relationName}.fieldName
// [+ __{childRelations} for every child relations]
});
}
};
However, I have no idea why RepositoryClassName__ accompanied with double underscore is needed to access the table column?
'RelationName.FieldName': 'DESC' will result in error instead.

Selectet data and data from with(), Laravel

I want to select some columns and some data from with(), the problem is that I get only data from select().
$today = date('Y-m-d', strtotime('-7 days'));
$contracts = Contract::select('
'contracts.id',
'contracts.contract_value_exc_VAT_total',
'customers.account_name',
'users.name',
)
->whereHas('dates', function($q) use($today){
return $q->whereDate('date', '>=', $today)
->where(function($q) {
$q->where('lkp_contract_date_tag_id', 4)
->orwhere('lkp_contract_date_tag_id', 7);
});
})
->with(['dates' => function($q){
$q->select('id', 'date');
}])
->join('customers','contracts.customer_id', 'customers.id')
->leftJoin('users','contracts.account_manager_select', 'users.id')
->get();
return response()->json($contracts);
From response, dates are null
//date....
dates: []
//date...
You can do it without using the select()
You can have the relations in the ContractModel. You can always process the data after getting from the database and manipulate it in a format you want to return.
There are two options to do that
Do it here in the controller itself.
Create an API resource for the Contract. (https://laravel.com/docs/7.x/eloquent-resources#introduction)
I would suggest the latter as it's more convenient.
For both of them you need to do this first. Make some changes in the contract model.
ContractModel.php
// I'm assuming that you have dates relation in the contract(because you've added it in the `with()` for eager loading.)
public function dates(){
...
}
// Instead of joining while doing the query, add the following
// relations in the contract as well.
public function customer(){
return $belongsTo('App\Customer', 'customer_id', 'id');
}
public function accountManagerSelect(){
return $belongsTo('App\User', 'account_manager_select', 'id');
}
This is how you go with the API resource approach.
Create the Contract Api Resource. And this is how the toArray() method should be.
toArray() {
// Get the dates in the format you want. I'have added the below
// format by considering the 'select' statement you added for
// dates.
$dates = [];
// will use the relation dates, to get the associated dates.
foreach($this->dates as $date){
array_push($dates, [
'id' => $date->id,
'date' => $date->date,
]);
}
return [
'id' => $this->id, // id of the contract.
'contract_value_exc_VAT_total' => $this
->contract_value_exc_VAT_total,
'account_name' => $this->account_name,
// This will use the accountManagerSelect relation to get the
// User instance and then you can access the name from that.
'name' => $this->accountManagerSelect->name,
'dates' => $dates, // The dates variable that we created earlier.
];
}
All you need to do is return using API resource in your controller.
instead of doing this
return response()->json($contracts);
Use Api resource
return ContractResource::collection($contracts);

Add a Field to a CrudController that ONLY passes values to Store / Update Methods

I'm trying to handle how a field within a CrudController stores or updates the data on the particular model in a completely custom way. I would like the traitStore() and traitUpdate() methods to ignore this field entirely, but would like the data to still be passed in via the request. This is specifically in reference to a many-many relationship using a select2_multiple field.
I would like it so that the relationship ID's are passed via the request object to the Store or Update methods, but I DO NOT want the traitStore() or traitUpdate() methods to actually perform updates on that particular field reference.
For example...
I have this field within my crud controller
$this->crud->addField(
[
'label' => "Groups",
'type' => 'select2_multiple',
'name' => 'groups',
'entity' => 'groups',
'attribute' => 'title',
'model' => "App\Models\Group",
'pivot' => true
]
);
And I'm overriding the Store and Update Methods like so.
public function store()
{
$this->crud->setValidation(UserRequest::class);
// WOULD LIKE TO SAVE EVERYTHING BUT IGNORE THE GROUPS FIELD
$response = $this->traitStore();
// DO WHATEVER I WANT WITH GROUPS AT THIS POINT
$groups = $request->groups
return $response;
}
public function update()
{
$this->crud->setValidation(UserRequest::class);
// WOULD LIKE TO SAVE EVERYTHING BUT IGNORE THE GROUPS FIELD
$response = $this->traitUpdate();
// DO WHATEVER I WANT WITH GROUPS AT THIS POINT
$groups = $request->groups
return $response;
}
Looking at my comments I would like to get a reference to the groups and handle updating the model however I want.
I've tried to unset the groups value in the request, unset($this->request{'groups'}), but it still updates / removes the relationships when I do that.
Here is what you need to do to remove the references from being updated by the CrudController.
public function update()
{
$this->crud->setValidation(UserRequest::class);
$request = clone $this->request;
$this->crud->request->request->remove('groups');
$this->crud->removeField('groups');
$groups = $request->groups
$response = $this->traitUpdate();
return $response;
}
I found an easy way to ignore/pass form field.
Example:
In your form fields have first_name, last_name, gender and in your database is only have fullname, gender then you wanna create/update the form, it will show Column not found: 'first_name' not found...,
How to fix it:
Add $fillable in the model and fill the array data with name field that you want to store/update. In example case $fillable = ['fullname', 'gender'];
Then, just use mutators inside the model too.
public function setFullnameAttribute(){
return $this->attributes['fullname'] = \Request::input('first_name') . ' ' . \Request::input('last_name');
}
NB: You should have hidden field name 'fullname' in your CrudController.
$this->crud->addField(['name' => 'fullname', 'type' => 'hidden']);

How to get specific fields or update theme and get the relation fildes from api calls in yii2 REST API?

I'm trying default yii2 api rest calls GET to get some model fields or PUT to update some model fields but I can't find a way to do this. I can only get all the fields or update theme all. Any help to do this? And how can I get the related relational field to this model?
I'm trying like this like
GET localhost/my-website-name/api/web/v1/vendors/
PUT localhost/my-website-name/api/web/v1/vendors/1
one way that I know for customizing fields is overriding fields function in your model like this
public function fields() {
return [
'id',
'iso3' => function() {
return base64_encode($this->iso3);
}
];
}
How to get specific fields and get the relation fields from api calls?
By default, yii\db\ActiveRecord::fields() returns all model attributes which have been populated from DB as fields, while yii\db\ActiveRecord::extraFields() should return the names of model's relations.
Take this model for example:
class Image extends ActiveRecord
{
public function attributeLabels()
{
return [
'id' => 'ID',
'owner_id' => 'Owner ID',
'name' => 'Name',
'url' => 'Url',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
public function getOwner()
{
return $this->hasOne(Owner::className(), ['id' => 'owner_id']);
}
public function extraFields()
{
return ['owner'];
}
}
Here I did override the extraFields() method to define the owner relationship. Now if I want to retreive all images but selecting id and name fields only and each resource should also hold its related owner data I would simply request this url:
GET example.com/images?fields=id,name&expand=owner
note: you can also use comma separation to expand more than one relation
In case you want to permanently remove some fields like created_at and updated_at you can also override the fields() method:
public function fields()
{
$fields = parent::fields();
unset($fields['created_at'], $fields['updated_at'], $fields['owner_id']);
return $fields;
/*
// or could also be:
return ['id', 'name','url'];
*/
}
this way the following request should only return image's id, name and url fields along with their related owner :
GET example.com/images?expand=owner
If owner's fields should be filtered too then override its fields() method too in its related class or a child class of it that you tie to the image model by using it when defining the relation.
See official documentation for further details.
PUT to update some model fields
Yii only updates dirty attributes. so when doing:
PUT example.com/images/1 {"name": "abc"}
the generated SQL query should only update the name column of id=1 inside database.
Hello i manage to find a solution for this situations
first get some model fields only and with related entities fildes:
public function fields() {
return [
'name',
'phone_number',
'minimum_order_amount',
'time_order_open',
'time_order_close',
'delivery_fee',
'halal',
'featured',
'disable_ordering',
'delivery_duration',
'working_hours',
'longitude',
'latitude',
'image',
'owner' => function() {
$owner = Owners::findOne($this->owner_id);
return array($owner);
}
];
}
if you want to remove some fildes
public function fields()
{
$fields=parent::fields();
unset($fields['id']);
return $fields;
}
update only specific fields
public function beforeSave($insert)
{
$lockedValues = ['name', 'halal', 'featured', 'latitude', 'longitude', 'image', 'status', 'created_at', 'updated_at'];
foreach ($lockedValues as $lockedValue) {
if ($this->attributes[$lockedValue] != $this->oldAttributes[$lockedValue])
throw new ForbiddenHttpException($lockedValue . " can't be changed.");
}
return parent::beforeSave($insert); // TODO: Change the autogenerated stub
}

Laravel - SELECT only certain columns within a `::with` controller function

The following code I have is working perfectly fine, however, it returns more data than what is necessary from each table:
public function getIndex()
{
$alerts = Criteria::with('bedrooms', 'properties')
->where('user_id', '=', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
What I'd like to do is, for example, select only the bedroom column out of the bedroom table. As it stands now, it returns all columns.
I have tried:
public function getIndex()
{
$alerts = Criteria::with('bedrooms' => function($q){
$q->select('bedroom');
}, 'properties')
->where('user_id', '=', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
But I am presented with the following error:
syntax error, unexpected '=>' (T_DOUBLE_ARROW)
Any help as to how I can achieve this will be hugely appreciated.
Update
public function getIndex()
{
$alerts = Criteria::with(
['coordinate' => function($w){
$w->select('name', 'id');
}],
['bedrooms' => function($q){
$q->select('bedroom', 'criteria_id');
}]
, 'properties')
->where('user_id', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}
The correct select query works for which ever is queried first. If I swap the queries around, the bedroom function works correctly, but the rest aren't eager loaded nor does the select query work.
Just pass an array there:
// note [ and ]
$alerts = Criteria::with(['bedrooms' => function($q){
$q->select('bedroom', 'PK / FK');
}, 'properties'])
Also mind that you need to select the keys of that relation (primary key/foreign key of the relation).
The answer to your update question is that you need to eager load other values in the same array some thing like this.
public function getIndex()
{
$alerts = Criteria::with(
['coordinate' => function($w)
{
$w->select('name', 'id');
},
'bedrooms' => function($q)
{
$q->select('bedroom', 'criteria_id');
}
, 'properties'])
->where('user_id', Auth::id())
->get();
$this->layout->content = View::make('users.alert.index',
array('alerts' => $alerts));
}