Adding a search box to filter a list of results in Symfony? - list

I need to put a search box within a list of objects as a result of a typical indexSuccess action in Symfony. The goal is simple: filter the list according to a criteria.
I've been reading the Zend Lucene approach in Jobeet tutorial, but it seems like using a sledge-hammer to crack a nut (at least for my requirements).
I'm more interested in the auto-generated admin filter forms but I don't know how to implement it in a frontend.
I could simply pass the search box content to the action and build a custom query, but is there any better way to do this?
EDIT
I forgot to mention that I would like to have a single generic input field instead of an input field for each model attribute.
Thanks!

I'm using this solution, instead of integrating Zend Lucene I manage to use the autogenerated Symonfy's filters. This is the way i'm doing it:
//module/actions.class.php
public function executeIndex(sfWebRequest $request)
{
//set the form filter
$this->searchForm = new EmployeeFormFilter();
//bind it empty to fetch all data
$this->searchForm->bind(array());
//fetch all
$this->employees = $this->searchForm->getQuery()->execute();
...
}
I made a search action which does the search
public function executeSearch(sfWebRequest $request)
{
//create filter
$this->searchForm = new EmployeeFormFilter();
//bind parameter
$fields = $request->getParameter($this->searchForm->getName());
//bind
$this->searchForm->bind($fields);
//set paginator
$this->employees = $this->searchForm->getQuery()->execute();
...
//template
$this->setTemplate("index");
}
It's important that the search form goes to mymodule/search action.
Actually, i'm also using the sfDoctrinePager for paginate setting directly the query that the form generate to get results properly paginated.
If you want to add more fields to the search form check this :)

I finally made a custom form using the default MyModuleForm generated by Symfony
public function executeIndex {
...
// Add a form to filter results
$this->form = new MyModuleForm();
}
but displaying only a custom field:
<div id="search_box">
<input type="text" name="criteria" id="search_box_criteria" value="Search..." />
<?php echo link_to('Search', '#my_module_search?criteria=') ?>
</div>
Then I created a route named #my_module_search linked to the index action:
my_module_search:
url: my_module/search/:criteria
param: { module: my_module, action: index }
requirements: { criteria: .* } # Terms are optional, show all by default
With Javascript (jQuery in this case) I append the text entered to the criteria parameter in the href attribute of the link:
$('#search_box a').click(function(){
$(this).attr('href', $(this).attr('href') + $(this).prev().val());
});
And finally, back to the executeIndex action, I detect if text was entered and add custom filters to the DoctrineQuery object:
public function executeIndex {
...
// Deal with search criteria
if ( $text = $request->getParameter('criteria') ) {
$query = $this->pager->getQuery()
->where("MyTable.name LIKE ?", "%$text%")
->orWhere("MyTable.remarks LIKE ?", "%$text%")
...;
}
$this->pager->setQuery($query);
...
// Add a form to filter results
$this->form = new MyModuleForm();
}
Actually, the code is more complex, because I wrote some partials and some methods in parent classes to reuse code. But this is the best I can came up with.

Related

hook_form_FORM_ID_alter: Pre select a checkbox from an exposed filter in a drupal 8 view

I have a view that lists blog articles. The blog content type has a taxonomy reference field to the 'tags' vocabulary, authors can select 1 or multiple tags. The view exposes the 'Has taxonomy terms (with depth) (exposed)' filter (as a list of checkboxes) so that users can search for blog articles containing 1 or more tags.
Now, i'm trying to pre-select 1 of the checkboxes that are exposed to the user in the hook_form_FORM_ID_alter() hook. It should be a simple as the code below but it just doesn't work. The tag i'm trying to pre-select has the ID 288.
What am i doing wrong? Thx...
function xtheme_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id)
{
if($form['#id'] == 'views-exposed-form-vcon-finder-page-1'){
$form['tags']['#default_value'] = [288 => 288];
}
}
You have to set user input like this:
function xtheme_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if($form['#id'] == 'views-exposed-form-vcon-finder-page-1'){
if (is_null(\Drupal::request()->get('tags'))) {
// Avoid overriding the filter values selected by user
$input = $form_state->getUserInput();
$input['tags'] = [288 => 288];
$form_state->setUserInput($input);
}
}
}

Custom form field in views

I'm trying to create a custom field for a view in Drupal 8 which allows to perform an action without changing the page (link). I guess I have to create a form inside that custom field but I do not know how to achieve it.
Any idea on how to do it or other alternative without redirecting to a route?
The view will be a list of custom entities and I need a button for each of the lines.
Thanks in advance!
Finally I solved it by following this steps:
I created a view custom field (generate:plugin:views:field with
drupal console)
I created a form (generate:form)
Then, in the view custom field render function return the form:
$form = \Drupal::formBuilder()->getForm('Drupal\test_module\Form\TestForm', $values->_entity->ID());
return $form;
It's important to notice that an incremental (dynamic) formId is needed for things to work properly. I did that by creating a static variable and a __construct() method as follows:
protected static $instanceId;
public function getFormId() {
return 'my_form_id_' . self::$instanceId;
}
public function __construct(){
if (empty(self::$instanceId)) {
self::$instanceId = 1;
}
else {
self::$instanceId++;
}
}

django-autocomplete-light add parameter to query url

I'm trying to pass some data along to the autocomplete_light.AutocompleteModelBase so I can exclude some models from the search. I'm trying to use the Dependencies info in the docs here
but I can seem to get it.
The id of the input is id_alternate_version-autocomplete, so I'm trying:
$("#id_alternate_version-autocomplete").yourlabsWidget().autocomplete.data = {'id': 'foo'};
But the url called looks like http://127.0.0.1:8000/autocomplete/FooAutocomplete/?q=bar
I want: http://127.0.0.1:8000/autocomplete/FooAutocomplete/?q=bar&id=foo
How can I do something like that?
DAL provides a way to do this with "forwarding" of another rendered form field's value.
See http://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#filtering-results-based-on-the-value-of-other-fields-in-the-form
This is how I did it:
$(document).ready(function() {
$('form#recipe').on('change propertychange keyup input paste', function() {
var ingredient_item_type = $("form#recipe input[type='radio']:checked").val();
var widget = $("form#recipe input#id_ingredients_text").parents('.autocomplete-light-widget');
if(ingredient_item_type) {
widget.yourlabsWidget().autocomplete.data['hello'] = 'world';
}
});
});
Javascript acrobatics aside, the key observation is thus:
anything you put in the .data object of the autocomplete widget will
automatically be made part of the GET request. HTH.

tt_address: add categorys of address to the template

Is it somehow possible to add the sub-group of a cetrain group the address is assigned to the html output?
In the template I have ###MAINGROUP### and ###GROUPLIST###. I can't use maingroup, cause it's not the case that the group I need is always the maingroup. And with the grouplist I can't say which group is the sub-group of the one group.
Anyone have an idea how I could do it?
And in addition to that I also need the value of a self created field in the tt_address table.
Edit:
I try it like #lorenz say. What I have so far:
ext_localconf.php:
<?php
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['tt_address']['extraItemMarkerHook'][]
='EXT:txnextaddresssort/class.tx_next_address_sort_addmarkers.php:tx_next_address_sort_addmarkers';
class.tx_next_address_sort_addmarkers.php:
<?php
class tx_next_address_sort_addmarkers {
function extraItemMarkerProcessor(&$markerArray, &$address, &$lConf,
&$pObj) {
$lcObj = t3lib_div::makeInstance('tslib_cObj');
$lcObj->data = $address;
$markerArray['###SORTBEREICH###'] =
$lcObj->stdWrap($address['tx_nextaddresssort_sort_bereich'],
$lConf['tx_nextaddresssort_sort_bereich.']);
}
}
Extentionkey: next_address_sort
All I get is a blank screen, but no errors in apache log
No, there is no possibility to do that.
Yet you can write a custom extension that integrates the extraItemMarkerProcessorhook in tt_address. In ext_localconf.php, add:
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['tt_address']['extraItemMarkerHook'][] ='EXT:myextension/class.tx_myextension_filename.php:tx_myextension_classname';
Then add a file class.tx_myextension_filename.php to your extension.:
class tx_myextension_classname {
public function extraItemMarkerProcessor(&$markerArray, &$address, &$lConf, &$pObj) {
$lcObj = t3lib_div::makeInstance('tslib_cObj');
$lcObj->data = $address;
$markerArray['###MYFIELD###'] = $lcObj->stdWrap($address['myfieldlikeindatabase'], $lConf['myfieldlikeindatabase.']);
return $markerArray;
}
}
This would be an example for getting a field that is in the tt_address table and adding it to the markers so they can be used in a template. It is also stdWrap enabled.
Now, instead of getting a field, you should replace $address['myfieldlikeindatabase'] with a variable that contains the information you need. To receive the data, you can use the TYPO3 database API functions ($GLOBALS['TYPO3_DB']).

Set queryable source on Rendering Parameter Template field

I have a Rendering Parameter template applied to a sublayout. It has a single Droptree field on it, and I want to set the Source of that field to a Sitecore query so I can limit the options available for that field.
Source can be:
query:./*
or
query:./ancestor-or-self::*[##templatename='MyTemplate']/
The query just needs to grab items relative to the content item that we're on. This normally works with Droptree fields in the content editor.
However I'm finding that the query isn't working here because we're in the rendering parameters, so it's not using the content item as it's context.
The query fails and I just get the full Sitecore tree.
I found this can be fixed up for the Datasource field with 'Queryable Datasource Locations' at this link:-
http://www.cognifide.com/blogs/sitecore/reduce-multisite-chaos-with-sitecore-queries/
However I don't know where to start to get this working for other rendering parameter fields.
Any ideas? (I'm using Sitecore 6.6 Update 5)
Unfortunately, the pipeline mentioned in Adam Najmanowicz's answer works for some other types, like Droplink and Multilist, but the pipeline isn't run for Droptree fields.
After looking into this deeper I found that the Source of a Droptree field IS using the wrong context item, as Adam mentioned, but the code comes from the Droptree field itself:-
Sitecore.Shell.Applications.ContentEditor.Tree, Sitecore.Kernel
Utilising the query string code from Adam's answer, we can create a 'fixed' Droptree custom field, that is almost the same as the regular Droptree but will use the correct context item instead.
The code will inherit from the normal Tree control, and only change the way that the Source property is set.
public class QueryableTree : Sitecore.Shell.Applications.ContentEditor.Tree
{
// override the Source property from the base class
public new string Source
{
get
{
return StringUtil.GetString(new string[]
{
base.Source // slightly altered from the original
});
}
set
{
Assert.ArgumentNotNull(value, "value");
if (!value.StartsWith("query:", StringComparison.InvariantCulture))
{
base.Source = value; // slightly altered from the original
return;
}
Item item = Client.ContentDatabase.GetItem(this.ItemID);
// Added code that figures out if we're looking at rendering parameters,
// and if so, figures out what the context item actually is.
string url = WebUtil.GetQueryString();
if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
{
FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
var currentItemId = parameters["contentitem"];
if (!string.IsNullOrEmpty(currentItemId))
{
Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
item = Sitecore.Data.Database.GetItem(contentItemUri);
}
}
if (item == null)
{
return;
}
Item item2 = item.Axes.SelectSingleItem(value.Substring("query:".Length));
if (item2 == null)
{
return;
}
base.Source = item2.ID.ToString(); // slightly altered from the original
}
}
The above code is pretty much the same as the Source property on the base Tree field, except that we figure out the proper context item from the URL if we've detected that we're in the rendering parameters dialog.
To create the custom field, you just need to edit the Web.Config file as described here. Then add the custom field to the core database as described here.
This means that parameters can now have queries for their source, allowing us to limit the available items to the content editor. (Useful for multi-site solutions).
The key here would be to set the Field Editor's context to be relative to the item you are editing instead of the Rendering parameters (that I think it has by default).
So you could have processor:
public class ResolveRelativeQuerySource
{
public void Process(GetLookupSourceItemsArgs args)
{
Assert.IsNotNull(args, "args");
if (!args.Source.StartsWith("query:"))
return;
Item contextItem = null;
string url = WebUtil.GetQueryString();
if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
{
FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
var currentItemId = parameters["contentitem"];
if (!string.IsNullOrEmpty(currentItemId))
{
Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
contextItem = Sitecore.Data.Database.GetItem(contentItemUri);
}
}
else
{
contextItem = args.Item;
}
}
}
hooked as:
<sitecore>
<pipelines>
<getLookupSourceItems>
<processor patch:before="*[#type='Sitecore.Pipelines.GetLookupSourceItems.ProcessQuerySource, Sitecore.Kernel']"
type="Cognifide.SiteCore.Logic.Processors.ResolveRelativeQuerySource, Cognifide.SiteCore" />
</getLookupSourceItems>
</pipelines>
</sitecore>
Together with ResolveQueryableDatasources from Przemek's blog this should solve your problem.