Laravel Scout: only search in specific fields - laravel-scout

Laravel Scout: Is there a way that I search in only a specific field?
At the moment this line is working fine:
$es = Element::search($q)->get();
But it searches title, shortdescription and description fields. I need it to only search in title field.

You just need to change your toSearchableArray method from your model by adding the following code:
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
$array = $this->only('title', 'description');
$related = $this->user->only('name', 'email');
// Customize array...
return array_merge($array, $related);
}
Then call php artisan scout:import "App\YourModel" to reindex the new records. 
Note:
$this->only('title', 'description') will search only for its title and description fields
$this->user->only('name', 'email') will also search for its name and email from a related Model
So you can retrieve the related data by adding ->load('user') in your search method like the following code:
public function search(Request $request)
{
$query = $request->get('q');
return Task::search($query)->get()->load('user');
}
UPDATE
If you're trying to retrieve the data using ->paginate() method, you must need to load the relations separately:
...
$tasks = Task::search($query)->paginate($request->get('per_page'));
$tasks->load('user');
return $tasks;
Enjoy!

for meilisearch this worked
Element::search('test',
function ($searchEngine, string $query, array $options) use ($filter) {
$searchEngine->resetSearchableAttributes();
$searchEngine->updateSearchableAttributes(['field_name']);
return $searchEngine->search($query, $options);
}
)

You can do that by adding a callback function to the scout builder instance,
Person::search($searchString)->query(function($query) {
$query->select(['title']);
})->get();

You can do that by adding a callback function to the scout builder instance,
Person::search($searchString)->query(function($query) {
$query->addSelect(['title']);
})->get();
Worked on laravel 7

If you want standard query result, but search only in a specific column (field), you can try this solution:
Element::search($query)->rule(function($builder) {
return [
'must' => [
'match' => [
'some_column_name' => $builder->query
]
]
];
});
Tested on Laravel 6, but I think it will work on later versions to...

Related

Symfony4 returning serialized json repsonse

I'm looking at a new Symfony5 project , where I'm trying to return a JSON response of some data.
I have a Project and a ProjectItem
I have the following:
// Project.php
/**
* #ORM\OneToMany(targetEntity="App\Entity\ProjectItem", mappedBy="project")
*/
private $projectItems;
// ProjectItem.php
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="projectItems")
*/
private $project;
I have one Project, that can have many ProjectItems
I then have a controller that I'm trying to return a json response:
public function index()
{
$itemsList = $this->getDoctrine()
->getRepository(Project::class)
->findAll();
$items = $this->get('serializer')->serialize($itemsList, 'json');
return new Response($items, 200);
}
This is currently returning an error:
A circular reference has been detected when serializing the object of class "App\Entity\Project" (configured limit: 1)
Am I using the serializer correctly or are my models incorrectly configured?
Simply use json_encode:
public function index()
{
$itemsList = $this->getDoctrine()
->getRepository(Project::class)
->findAll();
return new Response(
json_encode($itemsList),
200
);
}
or use JsonResponse class:
return new JsonResponse($itemsList);
You have a circular reference with your relations. Im guessing ProjectItem has a field project that is referencing Project which causes a loop for the serializer. You can ignore said attribute to prevent this from happening. Checkout the ignored attributes section of the serializer documentation.
Another option would be to use Serialization Groups. Every property would get a Group annotation like for example #Groups("group1") excluding that reference property back to Project.
You would then tell the serializer to serialize that group:
$json = $serializer->serialize(
$itemList,
'json', ['groups' => 'group1']
);
You may also checkout JMS Serializer which adds #Exclude and #Include annotations to make this step a bit easier.

XCART-5 Get Attributes Values in programming and Assigning some other values progmatically

I am working on Xcart-5 website customization. And I created my own module and doing work on that. I just created some Global Attributes (" As a Plain text ") field and assign these attributes to some product. Now I want to access these fields value in programming in the product details page for assigning some other value programatically at run time.
How can I achieve this task. Kindly provide me the solution .
In your module you should decorate the \XLite\Model\Attribute class and extend the getAttributeValue() method there.
For instance, if I use a module with developer ID Tony and module ID AttributesDemo, then I would need to create the XCartDirectory/classes/XLite/Module/Tony/AttributesDemo/Model/Attribute.php file with the following content:
<?php
// vim: set ts=4 sw=4 sts=4 et:
namespace XLite\Module\Tony\AttributesDemo\Model;
/**
* Attribute
* #MappedSuperClass
*/
abstract class Attribute extends \XLite\Model\AttributeAbstract implements \XLite\Base\IDecorator
{
public function getAttributeValue(\XLite\Model\Product $product, $asString = false)
{
$result = parent::getAttributeValue($product, $asString);
if (!$asString) {
foreach ($result as $obj) {
if ($obj->asString() == 'Mac') {
$obj->getAttributeOption()->setName('Windows');
}
}
}
return $result;
}
}
Such implementation will change Mac values to Windows ones in all attributes.

Ignore a Doctrine2 Entity when running schema-manager update

I've got a Doctrine Entity defined that maps to a View in my database. All works fine, the Entity relations work fine as expected.
Problem now is that when running orm:schema-manager:update on the CLI a table gets created for this entity which is something I want to prevent. There already is a view for this Entity, no need to create a table for it.
Can I annotate the Entity so that a table won't be created while still keeping access to all Entity related functionality (associations, ...)?
Based on the original alswer of ChrisR inspired in Marco Pivetta's post I'm adding here the solution if you're using Symfony2:
Looks like Symfony2 doesn't use the original Doctrine command at:
\Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand
Instead it uses the one in the bundle:
\Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand
So basically that is the class that must be extended, ending up in having:
src/Acme/CoreBundle/Command/DoctrineUpdateCommand.php:
<?php
namespace App\Command;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class DoctrineUpdateCommand extends UpdateSchemaDoctrineCommand
{
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): ?int
{
$ignoredEntities = [
'App\Entity\EntityToIgnore',
];
$metadatas = array_filter($metadatas, static function (ClassMetadata $classMetadata) use ($ignoredEntities) {
return !in_array($classMetadata->getName(), $ignoredEntities, true);
});
return parent::executeSchemaCommand($input, $output, $schemaTool, $metadatas, $ui);
}
}
Eventually it was fairly simple, I just had to subclass the \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand into my own CLI Command. In that subclass filter the $metadatas array that's being passed to executeSchemaCommand() and then pass it on to the parent function.
Just attach this new subclassed command to the ConsoleApplication you are using in your doctrine cli script and done!
Below is the extended command, in production you'll probably want to fetch the $ignoredEntities property from you config or something, this should put you on the way.
<?php
use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class My_Doctrine_Tools_UpdateCommand extends UpdateCommand
{
protected $name = 'orm:schema-tool:myupdate';
protected $ignoredEntities = array(
'Entity\Asset\Name'
);
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui)
{
/** #var $metadata \Doctrine\ORM\Mapping\ClassMetadata */
$newMetadata = [];
foreach ($metadatas as $metadata) {
if (!in_array($metadata->getName(), $this->ignoredEntities)) {
$newMetadata[] = $metadata;
}
}
return parent::executeSchemaCommand($input, $output, $schemaTool, $newMetadata, $ui);
}
}
PS: credits go to Marco Pivetta for putting me on the right track. https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/rwWXZ7faPsA
Quite old one but there is also worth nothing solution using Doctrine2: postGenerateSchema event listener - for me it's better than overriding
Doctrine classes:
namespace App\Doctrine\Listener;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
/**
* IgnoreTablesListener class
*/
class IgnoreTablesListener
{
private $ignoredTables = [
'table_name_to_ignore',
];
public function postGenerateSchema(GenerateSchemaEventArgs $args)
{
$schema = $args->getSchema();
$tableNames = $schema->getTableNames();
foreach ($tableNames as $tableName) {
if (in_array($tableName, $this->ignoredTables)) {
// remove table from schema
$schema->dropTable($tableName);
}
}
}
}
Also register listener:
# config/services.yaml
services:
ignore_tables_listener:
class: App\Doctrine\Listener\IgnoreTablesListener
tags:
- {name: doctrine.event_listener, event: postGenerateSchema }
No extra hooks is necessary.
In Doctrine 2.7.0 it was introduced the new SchemaIgnoreClasses entity manager config option that basically ignores the configured classes from any schema action.
To use it with Symfony we only need to add the schema_ignore_classes key in the Doctrine entity manager configuration like this:
doctrine:
dbal:
# your dbal configuration
orm:
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
Main:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity/Main'
prefix: 'App\Entity\Main'
alias: Main
schema_ignore_classes:
- Reference\To\My\Class
- Reference\To\My\OtherClass
$schema->getTableNames() was not working (I don't know why).
So:
<?php
namespace AppBundle\EventListener;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
class IgnoreTablesListener extends UpdateSchemaDoctrineCommand
{
private $ignoredEntities = [
'YourBundle\Entity\EntityYouWantToIgnore',
];
/**
* Remove ignored tables /entities from Schema
*
* #param GenerateSchemaEventArgs $args
*/
public function postGenerateSchema(GenerateSchemaEventArgs $args)
{
$schema = $args->getSchema();
$em = $args->getEntityManager();
$ignoredTables = [];
foreach ($this->ignoredEntities as $entityName) {
$ignoredTables[] = $em->getClassMetadata($entityName)->getTableName();
}
foreach ($schema->getTables() as $table) {
if (in_array($table->getName(), $ignoredTables, true)) {
// remove table from schema
$schema->dropTable($table->getName());
}
}
}
}
And Register a service
# config/services.yaml
services:
ignore_tables_listener:
class: AppBundle\EventListener\IgnoreTablesListener
tags:
- {name: doctrine.event_listener, event: postGenerateSchema }
Worked fine! ;)
If problem is only with producing errors in db_view, when calling doctrine:schema:update command, why not simplest way:
remove # from #ORM\Entity annotation
execute doctrine:schema:update
add # to ORM\Entity annotation
;-)

Zend Framework 2 - Doctrine 2 Error message

I have been trying to add more flexibilities to the Album Module from Zend Framework 2. In that process I have been trying to set a validator for one of the form fields especially for the album name which in my case the column name in my database is title.
I have been following the validation part from one of the previous answers to my post, which can be found here
I have been using that class in my albumcontroller class in this fashion:
<?php
namespace Album\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Album\Entity\Album\Album;
use Album\Form\AlbumForm;
use Album\Model\Album\AlbumExists;
use Doctrine\ORM\EntityManager;
class AlbumController
extends AbstractActionController
{
public function addAction()
{
$form = new AlbumForm();
$form->get('submit')->setAttribute('value', 'Add');
$query = "SELECT a.title FROM Album\Entity\Album\Album a";
$albumExists = new AlbumExists($this->getEntityManager(), $query, 'title');
$request = $this->getRequest();
if ($request->isPost())
{
$album = new Album();
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
$title = $this->getRequest()->getPost('title');
if ($form->isValid() && $albumExists->isValid($title))
{
$album->populate($form->getData());
$this->getEntityManager()->persist($album);
$this->getEntityManager()->flush();
return $this->redirect()->toRoute('album');
}
}
return array('form' => $form);
}
When I enter a album name/title which is already in the database it throws an error in this fashion:
An error occurred during execution; please try again later.
Additional information:
Doctrine\ORM\Query\QueryException
File:
C:\vendor\doctrine\orm\lib\Doctrine\ORM\Query\QueryException.php:69
Message:
Invalid parameter number: number of bound variables does not match number of tokens.
Any idea where Im making a mistake?
In case you're using "my" class and haven't modified that part, you're missing the WHERE condition in your query.
In the class, a parameter :value is bound, so you have to use this parameter in your query (e.g. WHERE a.title = :value).

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

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.