Need a faster eloquent search query - eloquent

I have two models user and actor with one to one relation. User table has name, email(unique) and user type(actor/filmmaker) column.
I want to get all actors details. For that, I am using the following code.
$actorlist= Actor::with('user')->paginate(15);
but it is very slow when the number of actors is more.
Also for search functionality, I am using the following code.
*$actorlist = Actor::join('users', function ($join) {
$join->on('actors.user_id', '=', 'users.id');
})->newQuery();
if ($request->has('has_voice')) {
$actorlist->has('voices');
}
if ($request->has('has_video')) {
$actorlist->has('shows');
}
if ($request->has('has_imdb')) {
$actorlist->where('imdb_link', '!=', null);
}
if ($request->has('hair_colour') && $request->input('hair_colour') != 'any hair colour') {
$actorlist->where('hair_colour', 'LIKE', $request->input('hair_colour'));
}
if ($request->has('gender') && $request->input('gender') != 'any gender') {
$actorlist->where('gender', 'LIKE', $request->input('gender'));
}
if ($request->has('ethnicity') && $request->input('ethnicity') != 'any ethnicity') {
$actorlist->where('ethnicity', 'LIKE', "%{$request->input('ethnicity')}%");
}
if ($request->has('eye_colour') && $request->input('eye_colour') != 'any eye colour') {
$actorlist->where('eye_colour', 'LIKE', $request->input('eye_colour'));
}
if ($request->has('hair_colour') && $request->input('hair_colour') != 'any hair colour') {
$actorlist->where('hair_colour', 'LIKE', $request->input('hair_colour'));
}
if ($request->has('min_age')) {
$actorlist->where('min_age', '>=', $request->input('min_age'));
}
if ($request->has('max_age')) {
$actorlist->where('max_age', '<=', $request->input('max_age'));
}
if ($request->has('min_height') && $request->has('max_height')) {
$actorlist->whereBetween('height', [floatval($request->input('min_height')), floatval($request->input('max_height'))]);
}
if($request->has('name') && $request->input('name') != ''){
$keywords = $searchValues = preg_split('/\s+/', $request->input('name'), -1, PREG_SPLIT_NO_EMPTY);
$actorlist->where(function ($q) use ($keywords) {
foreach ($keywords as $value) {
$q->orWhere('users.first_name', 'like', "%{$value}%");
$q->orWhere('users.last_name', 'like', "%{$value}%");
}
});
}
$actorlist = $actorlist->paginate(15);*
This is taking almost 5mins to load.
Please help me with feasible solutions.
Thanks in advance.

Related

How can I chain having() clause on my eloquent request

I have to make a request to filter some datas on my laravel app (v8).
I'm using livewire to filtering datas on a table.
The full request :
$datas = Client::withSum(
[ 'statistics_products as statistics_products_sum_'.$this->n_1 => fn ($query) => $query->where('yr', $this->n_1)],
'total_ht')
->withSum(
[ 'statistics_products as statistics_products_sum_'.$this->n_2 => fn ($query) => $query->where('yr', $this->n_2)],
'total_ht')
->withSum(
[ 'statistics_products as statistics_products_sum_'.$this->n => fn ($query) => $query->where('yr', $this->n)],
'total_ht')
// For is_deleted selection
->when($this->deleted, function ($query, $deleted) {
if($deleted == 'oui') {
$query->where('is_delete', 1);
} elseif($deleted === 'non') {
$query->where('is_delete', 0);
}
})
// For dpt selection
->when($this->dpt, function ($query, $dpt) {
$query->where('postal_code', 'LIKE', $this->dpt. '%');
})
// For referent selection
->when($this->referent, function ($query, $referent) {
if($referent === 'oui') {
$query->whereColumn('client_num', 'sign_num');
} elseif($referent === 'non') {
$query->whereColumn('client_num', '!=', 'sign_num');
}
})
->when($this->ca, function ($query, $ca) {
if($this->year != null) {
if($ca === 'A') {
$query->having('statistics_products_sum_'.$this->year, '<', 20000)->orHaving('statistics_products_sum_'.$this->year, '=', null);
}elseif($ca === 'B') {
$query->havingBetween('statistics_products_sum_'.$this->year, [20001, 40000]);
}elseif($ca === 'C') {
$query->havingBetween('statistics_products_sum_'.$this->year, [40001, 60000]);
}elseif($ca === 'D') {
$query->havingBetween('statistics_products_sum_'.$this->year, [60001, 100000]);
}elseif($ca === 'E') {
$query->havingBetween('statistics_products_sum_'.$this->year, [100001, 500000]);
}elseif($ca === 'F') {
$query->having('statistics_products_sum_'.$this->year, '>', 500000);
}
}
})
// For search
->when($this->search, function ($query, $search) {
$query->searchIn3Columns('society_name', 'city','postal_code', $search);
})
->paginate($this->pagination_number);
But this part doesn't work properly :
if($ca === 'A') {
$query->having('statistics_products_sum_'.$this->year, '<', 20000)
->orHaving('statistics_products_sum_'.$this->year, '=', null);
}
Here I only get results for 'statistics_products_sum_'.$this->year, '<', 20000
I don't get the result where it's null...
Do you know how can I chain two having clause?
This part of the query doesn't seem right:
->orHaving('statistics_products_sum_'.$this->year, '=', null); // = null ???
Maybe try with:
if ($ca === 'A') {
$query->having('statistics_products_sum_' . $this->year, '<', 20000)
->orWhereNull('statistics_products_sum_' . $this->year);
}

Discourse plugin to restrict users from mentioning each other is failing

I have inherited a plugin used to restrict #mentions in discourse. Users are restricted to specific categories and are unable to view blocked categories, but when using the #mention in a topic the users in the restricted categories are showing up.
So user A works at company 1 and has access to the category associated to company 1. User B has access to the company 2 category. When user A #mentions someone on the company 1 category the autocomplete is displaying the users associated with the company 2 category.
I'm receiving no errors and the plugin supposedly worked before my arrival.
import { withPluginApi } from "discourse/lib/plugin-api";
import discourseComputed from "discourse-common/utils/decorators";
import userSearch from "discourse/lib/user-search";
function initWithApi(api) {
if (!Discourse.SiteSettings.restrict_mentions_enabled) return;
api.modifyClass("component:groups-form-interaction-fields", {
pluginId: 'groups-form-interaction-fields-plugin',
#discourseComputed(
"siteSettings.restrict_mentions_enabled",
"currentUser.admin",
"model.c_all_groups",
"model.name"
)
isShowRestrictMentions(enabled, admin, allGroups, name) {
return enabled && admin && allGroups && name && allGroups.includes(name);
},
#discourseComputed("model.c_all_groups", "model.name")
cSelectableGroups(allGroups, name) {
return (allGroups || []).filter(g => g !== name);
},
actions: {
setCAllowedMentionGroups(val) {
console.log(val);
let newVal;
if (val.includes("any")) {
newVal = "any";
} else {
newVal = val.filter(x => !Ember.isBlank(x)).join("|");
}
console.log(newVal)
this.model.set("c_allowed_mention_groups", newVal);
}
}
});
api.modifyClass("model:group", {
pluginId: 'group-plugin',
asJSON() {
const attrs = this._super(...arguments);
attrs["c_allowed_mention_groups"] = this.c_allowed_mention_groups;
return attrs;
},
#discourseComputed("c_allowed_mention_groups")
cAllowedMentionGroups(groups) {
return (groups || "").split("|");
}
});
api.modifyClass("component:composer-editor", {
pluginId: 'composer-editor-plugin',
userSearchTerm(term) {
if (!this.siteSettings.restrict_mentions_enabled) {
return this._super(...arguments);
}
let viewGroups = true;
const allowed =
this.get("topic.c_allowed_mention_groups") ||
this.currentUser.get("c_allowed_mention_groups");
console.log([this, allowed]);
if (Ember.isBlank(allowed)) {
return;
}
//REMOVING CUSTOMER GROUP FROM SEARCHABLE ARRAY OF STANDARD USERS
if(!this.currentUser.admin && !this.currentUser.moderator){
viewGroups = false;
const index = allowed.indexOf('All_Customers');
if (index > -1) {
allowed.splice(index, 1);
}
console.log(allowed)
}
const opts = {
term,
includeGroups: viewGroups,
groupMembersOf: allowed
};
return userSearch(opts);
}
});
}
export default {
name: "restrict-mentions",
initialize() {
withPluginApi("0.8", initWithApi);
}
};

w2ui filter option "contains not" possible?

I am using w2ui (1.5) and I would be interested in whether it is possible to use a filter that only delivers negative results
That means only records/keys which not fullfilling a certain criteria.
Like a condition "contains not" or "is not" in addition to
http://w2ui.com/web/docs/1.5/w2grid.textSearch.
Thanks!
Gordon
okay, a possible solution is
w2ui['grid'].search([{ field: var1, value: var2, operator: 'not in'}], 'OR');
I coded my own solution to this problem. This adds a way to use "not" for string and "!=" for number searches.
This function does the search and it is also used to store the grid advanced search popup in history state.
I'm sure this can be even more optimized, so please use this more like a guideline. Hope this helps somebody.
function searchExtend(event, grid) {
// if event is null, we do just the local search
var searchObj;
if (event == null) {
searchObj = grid;
} else {
searchObj = event;
}
// make a copy of old data
const oldSearchData = structuredClone(searchObj.searchData);
const oldSearchLogic = structuredClone(searchObj.searchLogic);
var searchData = searchObj.searchData;
var invertedSdata = [];
var toSplice = [];
// check operator if it's "not" or "!="
for (var i = 0; i < searchData.length; i++) {
var sdata = searchData[i];
// invert the condition
if (sdata.operator == "not") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "contains",
value: sdata.value
});
}
if (sdata.operator == "!=") {
toSplice.push(i);
invertedSdata.push({
field: sdata.field,
type: sdata.type,
operator: "=",
value: sdata.value
});
}
}
// remove all "not" and "!=" from searchData
for (var i in toSplice) {
searchData.splice(i, 1);
}
var foundIds = [];
// use inverted criteria to search
if (invertedSdata.length > 0) {
grid.searchData = invertedSdata;
grid.searchLogic = "OR";
grid.localSearch();
grid.searchLogic = oldSearchLogic;
// store found ids
foundIds = structuredClone(grid.last.searchIds);
}
if (foundIds.length > 0) {
// perform a search with original criteria - spliced "not" and "!="
grid.searchData = searchData;
grid.localSearch();
var allRecIds = structuredClone(grid.last.searchIds);
// if there's not any results, push push all recIds
if (grid.last.searchIds.length == 0) {
for (let i = 0; i < grid.records.length; i++) {
allRecIds.push(i);
}
}
// remove all ids found with inverted criteria from results. This way we do the "not" search
for (const id of foundIds) {
allRecIds.splice(allRecIds.indexOf(id), 1);
}
if (event != null) {
// let the search finish, then refresh grid
event.onComplete = function() {
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
} else {
// refresh the grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
return;
}
if (event != null) {
event.onComplete = function() {
setSearchState(grid); // store state
}
} else {
// refresh whole grid
refreshGrid(grid, allRecIds, oldSearchData);
setSearchState(grid);
}
}
function refreshGrid(grid, allRecIds, oldSearchData) {
grid.last.searchIds = allRecIds;
grid.total = grid.last.searchIds.length;
grid.searchData = oldSearchData;
grid.refresh();
}
function setSearchState(grid) {
history.replaceState(JSON.stringify(grid.searchData), "Search");
}
To use this, you have to call it from the grid's onSearch:
onSearch: function(event) {
searchExtend(event, w2ui["grid"]);
}
Also, if you want to use the history.state feature, it needs to be called from onLoad function:
onLoad: function(event) {
event.onComplete = function() {
console.log("History state: " + history.state);
if (history.state != null) {
w2ui["grid"].searchData = JSON.parse(history.state);
searchExtend(null, w2ui["grid"]);
}
}
To add operators, please use this reference.
This is my solution to the problem:
operators: {
'text': ['is', 'begins', 'contains', 'ends', 'not'], // could have "in" and "not in"
'number': ['=', 'between', '>', '<', '>=', '<=', '!='],
'date': ['is', 'between', {
oper: 'less',
text: 'before'
}, {
oper: 'more',
text: 'after'
}],
'list': ['is'],
'hex': ['is', 'between'],
'color': ['is', 'begins', 'contains', 'ends'],
'enum': ['in', 'not in']
}

How can I programmatically create nested where clauses with Zend\Db\Sql component?

I've tried using the following code to accomplish it, but the unnest function returns an error:
foreach($params as $key=>$param) {
if(strpos($param, ',') !== false) {
$where = new \Zend\Db\Sql\Where();
$where->nest();
$param_arr = explode(',', $param);
$entered_once = 0;
foreach($param_arr as $p) {
$where->equalTo($key, $p);
if($entered_once < count($param_arr)) {
$where->or;
}
$entered_once++;
}
$where->unnest();
$select->where($where);
}
else {
$select->where(array($key => $param));
}
}
I've figured out what I'm doing wrong: I didn't realize that the nest() function returned a brand new PredicateSet and that's what I needed to call everything on. The solution is as follows:
foreach($params as $key=>$param) {
if(strpos($param, ',') !== false) {
$where = new \Zend\Db\Sql\Where();
$predicate_set = $where->nest();
$param_arr = explode(',', $param);
$entered_once = 0;
foreach($param_arr as $p) {
$predicate_set->equalTo($key, $p);
if($entered_once < count($param_arr)) {
$predicate_set->or;
}
$entered_once++;
}
$predicate_set->unnest();
$select->where($where);
}
else {
$select->where(array($key => $param));
}
}

how to validate zend form where at one field in required between two fields

I have two elements, adult no and children no, at least one field is required. How to validate this in zend framework and generate error message.
You need to create Your own validator. In this case i think You could use Zend_Validate_Identical, copy its code and change isValid method to something like this:
public function isValid($value, $context = null)
{
$this->_setValue((string) $value);
if (($context !== null) && isset($context) && array_key_exists($this->getToken(), $context)) {
$token = $context[$this->getToken()];
} else {
$token = $this->getToken();
}
if ($token === null) {
$this->_error(self::MISSING_TOKEN);
return false;
}
$strict = $this->getStrict();
// change != to ==
if (($strict && ($value === $token)) || (!$strict && ($value == $token)) && (&token =='' || $value == '') {
$this->_error(self::**YOUR_ERROR _CODE**);
return false;
}
return true;
}
This code is not tested but it should work :)