autocomplete, search multiple database tables/fields - Symfony - symfony1

I have 3 models:
article [name, title]
photo [name, description]
video [name, description, transcription]
Now, I want to create an autocomplete search that will search each of those models and db fields for matching string in the input field.
Is it possible to do a search over these 3 tables?
If so, how would I go about it?

UNION operator is what you're looking for.

I guess your using propel.
Its absolutetly possible to do this, but if these table are not related to anything that could result in some messy code when saving. But if you absolutely need to search in those tables, my approach would be to create an action like:
public function executeAutocomplete(sfWebRequest $request)
{
$q = $request->getParameter('q');//The value to be searched
$limit = $request->getParameter('limit');//Not completly necessary but recommendable
if(count($q) == 0)
{
return sfView::NONE;//With no input, no search
}
$results = array( 'article' => array(), 'photo' => array(), 'video' => array());
$article_search = ArticlePeer::search($q,$limit);
$photo_search = PhotoPeer::search($q,$limit);
$video_search = VideoPeer::search($q,$limit);
$this->process_search($results,$article_search,$photo_search,$video_search);
//Process those three arrays to fit your need
return $this->renderText(json_encode($result));
}
Now, the search function on the Peer classes could look like this:
ArticlePeer>>
public static function search($q,$limit = null)
{
$c = new Criteria();
$c->add(self::NAME,'%'.$q.'%',Criteria::LIKE);
if(!is_null($limit))
{
$c->addLimit($limit);
}
return self::doSelect($c);
}
Finally as for the widget, i have used and adapted sfWidgetJQueryAutocomplete and it work pretty good so you should check it out.
EDIT: The short way to an embbed search field si creating sfForm wit the jQuery widget i mentioned before and leave configuration and js to the widget. You'll have to find a way to handle search resutls.
Well i hope this helped you!

Related

How to Add a field in cascade of 2 many to one on Easyadmin 4 Symfony 6

I read and try a lots of things just to add a field witch is in relation.
One Dance have a level (beginner, improver...) and one Level have a Style (Country music, disco...). So for a dance I can get the level and associate style. Dance is MTO with Level, and Level is MTO with Style. It work fine in traditionnel controller and in Dance Index twig I can do
{{ dance.level.style }}
It's work fine.
Impossible for me to do that in EasyAdmin: In Danse Crud Controller
yield AssociationField::new('level');
is naturally working fine but how adding the style name? I'm not familiar with Queribuilder if it's the solution. I read Symfony Documentation easyadmin about unmapped fields but I don't undestand "createIndexQueryBuilder" parameters. If you can help me to progress. Thank's in advance
I don't find examples in stack with Easyadmin 4. And (I'm sorry), documentation is not very clear for me.
Example:
class UserCrudController extends AbstractCrudController
{
// ...
public function configureFields(string $pageName): iterable
{
return [
TextField::new('fullName'),
// ...
];
}
public function createIndexQueryBuilder(SearchDto $searchDto, EntityDto $entityDto, FieldCollection $fields, FilterCollection $filters): QueryBuilder
{
$queryBuilder = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
// if user defined sort is not set
if (0 === count($searchDto->getSort())) {
$queryBuilder
->addSelect('CONCAT(entity.first_name, \' \', entity.last_name) AS HIDDEN full_name')
->addOrderBy('full_name', 'DESC');
}
return $queryBuilder;
}
}
Why we have "entity.first_name" (why entity word and not entityDto...). dump parameters don't give me persuasive results
Easy finally.
You can choice the field you want to be rendered. Basically add __toString in Entity.
In my case just add for a many to many relation:
AssociationField::new('dances')
->setFormTypeOption('choice_label','level.style'),

how to use ilike with Integer in grails

I use EasyGrid plugin and must find values where integer field like '%001%'
initialCriteria {
ilike('id', "%"+params.id+"%")
}
But ilike doesn't work with Integer. How to do it?
I tried to do:
initialCriteria {
ilike('id'.toString(), "%"+params.id+"%")
}
initialCriteria {
ilike('str(id)', "%"+params.id+"%")
}
but it's not work.
If id is an integer in the database, then ilike doesn't really make much sense and there is probably a better way to do what you are trying to do (like adding a type field or something to the domain object, and filter by type)
However, you should be able to do something like this (untested):
initialCriteria {
sqlRestriction "cast( id AS char( 256 ) ) like '%001%'"
}
Following criteria not working if you search from your textbox when user search any text character by mistake like 12dfdsf as your searchable id. It will give you an exception
initialCriteria {
ilike('id', "%"+params.id+"%")
}
For better use you can use following criteria
initialCriteria {
sqlRestriction "id like '%${params?.id}%'"
}
You could do:
String paddedId = params.id.toString().padLeft(3,'0')
initialCriteria {
ilike('id', "%$paddedId%")
}
The solution offered by tim_yates with the sqlRestriction would work in version 1.5.0 of easygrid.
One of the main differences from 1.4.x is that the gorm datasource no longer uses DetachedCriteria, but Criteria - which maps directly to Hibernate's Criteria API.
So you can try it on the last version.
(Keep in mind that the upgrade might break your existing grids. There's also many other changes)
Another small observation is that 'initialCriteria' is not the right place to do stuff like that. (it's not wrong, but there is a 'globalFilterClosure' property for applying column independent filters)
I mixed the code posted by #tim_yates and mine:
String paddedId = params.id.toString().padLeft(3,'0')
def crit = Book.withCriteria {
sqlRestriction "lpad(cast( id AS char( 256 ) ), 3, '0') like '%${paddedId}%'"
}
I've tried with a h2 in-memory db and it works, but I am not sure about two things:
the real usefulness of that
lpad syntax consistence across all db engines
YMMV

Filter BindingSource with entity framework

Hi
How can i filter results exists in BindingSource filled with entities ( using EF 4)?
I tried this:
mybindingsource.Filter = "cityID = 1"
But it seems that binding source with entity framework doesn't support filtering .. am i right ?,is there another way to filter(search) data in binding source .
PS:
- I'm working on windows application not ASP.NET.
- I'm using list box to show the results.
Thanx
Maybe a better one than Leonid:
private BindingSource _bs;
private List<Entity> _list;
_list = context.Entities;
_bs.DataSource = _list;
Now when filtering is required:
_bs.DataSource = _list.Where<Entity>(e => e.cityID == 1).ToList<Entity>;
This way you keep the original list (which is retrieved once from the context) and then use this original list to query against in memory (without going back and forth to the database). This way you can perform all kinds of queries against your original list.
I think, you have made mistake in syntax. You should write Filter like this:
mybindingsource.Filter = "cityID = '1'"
Another way is to use LINQ expressions.
(About LINQ)
Why do you have to call Entety again?
Simple solution:
public List<object> bindingSource;
public IEnumerable FiltredSource
{
get{ return bindingSource.Where(c => c.cityID==1);
}
.where (Function (c) c.cityID = 1)

setBaseQuery not working on Tasks in Symfony

I'm building a Task in Symfony with Doctrine. I'm getting all the places to make a batch update to a particular field. This has to be done in two languages. I'm using setBaseQuery() to make the JOIN on the query with the language I want.
If I do the following in an action, it works without any problem. However, If I do it in a task; it doesn't work as expected. The task runs perfectly two times (one in english and the other in english too!).
Any ideas on what I have to do different on tasks?
thanks!
$languages = array('es' => 'Spanish', 'en' => 'English');
foreach($languages as $lang => $value) {
// get all the places
$q = Doctrine::getTable('Place')
->createQuery('p')
->leftJoin('p.Translation ptr')
->addWhere('ptr.lang = ?', $lang);
$treeObject = Doctrine::getTable('Place')->getTree();
$rootColumnName = $treeObject->getAttribute ( 'rootColumnName' );
$treeObject->setBaseQuery($q);
// all the continents
foreach ( $treeObject->fetchRoots() as $continent ) {
$this->log(date("Y-m-d H:i:s").' '.$lang.' Continent '.$continent->title);
..
}
}
To gain database access in your tasks you'll need to call the following in your execute() method before any other database calls:
$databaseManager = new sfDatabaseManager($this->configuration);
http://www.symfony-project.org/book/1_2/16-Application-Management-Tools#chapter_16_sub_custom_tasks_new_in_symfony_1_1
so the solution I've found is adding $model->Translation[$lang]->title. It's strange because the title should have been loaded through the setbasequery; but it seems not.
// all the continents
foreach ( $treeObject->fetchRoots() as $continent ) {
$this->log(date("Y-m-d H:i:s").' '.$lang.' Continent '.$continent->Translation[$lang]->title);
..
}
Update
I'm getting the translation but $continent->save() didn't save the changes in Spanish (however it's saving it in English). I had to do a manual query in Doctrine:
$con = Doctrine_Manager::getInstance()->connection();
...
$con->execute("update model_translation set field='".$field."' where id='".$model->id."' and lang='".$lang."'");
now everything works...

StackOverflowException caused by a linq query

edit #2: Question solved halfways. Look below
As a follow-up question, does anyone know of a non-intrusive way to solve what i'm trying to do below (namely, linking objects to each other without triggering infinite loops)?
I try to create a asp.net-mvc web application, and get a StackOverFlowException. A controller triggers the following command:
public ActionResult ShowCountry(int id)
{
Country country = _gameService.GetCountry(id);
return View(country);
}
The GameService handles it like this (WithCountryId is an extension):
public Country GetCountry(int id)
{
return _gameRepository.GetCountries().WithCountryId(id).SingleOrDefault();
}
The GameRepository handles it like this:
public IQueryable<Country> GetCountries()
{
var countries = from c in _db.Countries
select new Country
{
Id = c.Id,
Name = c.Name,
ShortDescription = c.ShortDescription,
FlagImage = c.FlagImage,
Game = GetGames().Where(g => g.Id == c.GameId).SingleOrDefault(),
SubRegion = GetSubRegions().Where(sr => sr.Id == c.SubRegionId).SingleOrDefault(),
};
return countries;
}
The GetGames() method causes the StackOverflowException:
public IQueryable<Game> GetGames()
{
var games = from g in _db.Games
select new Game
{
Id = g.Id,
Name = g.Name
};
return games;
}
My Business objects are different from the linq2sql classes, that's why I fill them with a select new.
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
edit #1: I have found the culprit, it's the following method, it triggers the GetCountries() method which in return triggers the GetSubRegions() again, ad nauseam:
public IQueryable<SubRegion> GetSubRegions()
{
return from sr in _db.SubRegions
select new SubRegion
{
Id = sr.Id,
Name = sr.Name,
ShortDescription = sr.ShortDescription,
Game = GetGames().Where(g => g.Id == sr.GameId).SingleOrDefault(),
Region = GetRegions().Where(r => r.Id == sr.RegionId).SingleOrDefault(),
Countries = new LazyList<Country>(GetCountries().Where(c => c.SubRegion.Id == sr.Id))
};
}
Might have to think of something else here :) That's what happens when you think in an OO mindset because of too much coffee
Hai! I think your models are recursively calling a method unintentionally, which results in the stack overflow. Like, for instance, your Subregion object is trying to get Country objects, which in turn have to get Subregions.
Anyhow, it always helps to check the stack in a StackOverflow exception. If you see a property being accessed over and over, its most likely because you're doing something like this:
public object MyProperty { set { MyProperty = value; }}
Its easier to spot situations like yours, where method A calls method B which calls method A, because you can see the same methods showing up two or more times in the call stack.
The problem might be this: countries have subregions and subregions have countries. I don't know how you implement the lazy list, but that might keep calling GetCountries and then GetSubRegions and so on. To find that out, I would launch the debugger en set breakpoints on the GetCountries and GetSubRegions method headers.
I tried similar patterns with LinqToSql, but it's hard to make bidirectional navigation work without affecting the performance to much. That's one of the reasons I'm using NHibernate right now.
To answer your edited question, namely: "linking objects to each other without triggering infinite loops":
Assuming you've got some sort of relation where both sides need to know about the other... get hold of all the relevant entities in both sides, then link them together, rather than trying to make the fetch of one side automatically fetch the other. Or just make one side fetch the other, and then fix up the remaining one. So in your case, the options would be:
Option 1:
Fetch all countries (leaving Subregions blank)
Fetch all Subregions (leaving Countries blank)
For each Subregion, look through the list of Countries and add the Subregion to the Country and the Country to the Subregion
Option 2:
Fetch all countries (leaving Subregions blank)
Fetch all Subregions, setting Subregion.Countries via the countries list fetched above
For each subregion, go through all its countries and add it to that country
(Or reverse country and subregion)
They're basically equialent answers, it just changes when you do some of the linking.

Resources