Zend Framework 2 - Doctrine 2 Error message - doctrine-orm

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).

Related

Laravel Scout: only search in specific fields

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...

'Invalid schema name' error thrown by Doctrine, but the raw SQL seems to work

I'm using some custom DQL functions to filter rows by some JSONB fields in PostgreSQL. Here's my query function:
private function findTopLevelResources(): array {
return $this->createQueryBuilder('r')
->where("JSON_EXISTS(r.contents, '-1') = FALSE")
->getQuery()
->getResult();
}
Running this code results in DriverException from AbstractPostgreSQLDriver:
An exception occurred while executing 'SELECT r0_.id AS id_0, r0_.marking AS marking_1, r0_.contents AS contents_2, r0_.kind_id AS kind_id_3 FROM resource r0_ WHERE r0_.contents?'-1' = false':
SQLSTATE[3F000]: Invalid schema name: 7 ERROR: schema "r0_" does not exist
LINE 1: ... r0_.kind_id AS kind_id_3 FROM resource r0_ WHERE r0_.conten...
^
I tried to execute the raw SQL query manually from PHPStorm and it worked, no errors.
How do I get this to work in Doctrine?
Why doesn't this query work with Doctrine, but does when I test it manually?
Here's JSON_EXISTS: (based on syslogic/doctrine-json-functions)
class JsonExists extends FunctionNode
{
const FUNCTION_NAME = 'JSON_EXISTS';
const OPERATOR = '?';
public $jsonData;
public $jsonPath;
public function getSql(SqlWalker $sqlWalker)
{
$jsonData = $sqlWalker->walkStringPrimary($this->jsonData);
$jsonPath = $this->jsonPath->value;
return $jsonData . self::OPERATOR . "'$jsonPath'";
}
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->jsonData = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->jsonPath = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
Registered via Symfony's YAML config like this:
doctrine:
orm:
dql:
numeric_functions:
json_exists: Syslogic\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonExists
Versions of stuff:
PHP 7.1.1
doctrine/dbal v2.6.1
doctrine/orm dev-master e3ecec3 (== 2.6.x-dev)
symfony/symfony v3.3.4
The error message is a false clue.
Actual problem is caused by PDO (this is why it works from PHPStorm). When it sees a query like this:
SELECT * FROM foo WHERE contents?'bar'
It treats it like a parametrized query and the question mark ? as a parameter. For some reason it sometimes results in nonsensical error messages. This specific case could be solved by adding a space after the question mark, but it won't work for operators ?| and ?& which can't have a space in the middle.
The solution is to use functions corresponding to operators (this question saved the day). One can find out how they are called using queries like this one:
SELECT oprname, oprcode FROM pg_operator WHERE oprname IN ('?', '?|', '?&')
Here's the part of result related to JSON:
? → jsonb_exists
?| → jsonb_exists_any
?& → jsonb_exists_all
So instead of previous query which causes problems via PDO, one can use this equivalent one:
SELECT * FROM foo WHERE jsonb_exists(contents, 'bar')

Doctrine : Translatable won't deliver translations for entities linked by #ManyToOne (or others associations)

Say you have a Category entity :
class Category
{
/**
* #Gedmo\Translatable
*/
private $categoryName;
}
And a Module entity.
Each Module belongs to one Category :
class Module
{
/**
* #Gedmo\Translatable
*/
private $moduleName;
/**
* #ManyToOne(targetEntity="Module")
*/
private $category;
}
Using the query hint I can get a Module translated to a specific locale :
function getModule($id, $locale)
{
$query = $em->createQuery('select m from AppBundle\Entity\Module m where m.id=' . $id);
$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, $locale);
$query->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
$module = $query->getSingleResult();
return $module;
}
Testing :
$module = getModule(1234, 'en');
// SUCCEED
$this->assertEquals('module name in english', $module->getModuleName();
$category = $module->getCategory();
// FAIL !
$this->assertEquals('category name in english', $category->getModuleName();
// SUCCEED (but one more request)
$category->setTranslatableLocale('en');
$em->refresh($category);
$this->assertEquals('category name in english', $category->getModuleName();
In few words : the query hint is able to insert a JOIN to the SELECT statement for the Module entity.
Call to $module->getCategory() will trigger lazy loading. But this time, there will be no translation JOINed.
Meaning I have to explicitly ask for the english version of the category.
Question : is it possible to have associated entities translated when querying the owner entity ? (having the FAILed test to succeed)
I already tried eager fetching on the ManyToOne association. Won't work because I am querying through DQL.
UPDATE :
To make this specific example to work (and have a better code) I can JOIN the Category in my DQL :
select m,c from Module join m.category where m.id=id
This will load the module and its category with the correct translations.
But in a perfect world I would love to have a generic code like this one :
protected function getEntityById($className, $id, $locale = null)
{
$dql = 'select e from AppBundle\Entity\\' . $className . ' e where e.id=' . $id;
$query = $this->em->createQuery($dql);
$query->setHint(
\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$locale
);
$query->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
$entity = $query->getSingleResult();
return $entity;
}
How to update this piece of code to have the associated entities to be loaded with the correct locale ?
I'm afraid there is no way to affect the behaviour of $module->getCategory(). Association's loading can only be affected by using doctrine filters. Unfortunately these filters can only add to WHERE part of request, so it is not possible to add join.
I'd create a function in category repository to get category with the proper translation like you did in the getModule(). I don't think there is another way. Ex:
$category = $categoryRepository->findByModule18n($module, $locale);

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.

Symfony 2 & Doctrine 2: Complicated relation beetwen entities

I have two entities. First called "Status":
<?php
class Status {
protected $id;
protected $type = null; // standarized type of status (f.e. "locked", "disabled")
protected $value = true; // true or false (value of status)
protected $change_reason = null;
protected $changed_by;
protected $changed_at;
}
I've cleared annotations for better readability.
And the second called eg. Account. Because Account is not the only one entity using Statuses, relations beetwen Status and any other "statusable" entity (I think) should be many-to-many. For Account there would be join table account_status etc.
Additionaly one status belongs to only one Entity.
It all works in that configuration, but I really don't know how to retrieve a list of accounts with their latest statuses.
I wrote an SQL query to retrieve actual statuses:
SELECT * FROM (
SELECT t.type, t.value
FROM status AS t
ORDER BY t.changed_at DESC
) AS t1 GROUP BY t1.type
My questions are:
Is that idea correct at all?
How to retrieve a list of accounts with all their latest statuses?
Sorry for my poor English.
EDIT:
I want just to get an account, join its latest statuses, and then get them simply by: $task -> getStatus('highlighted') to get a value of a latest (youngest) status of type "highlighted"
EDIT2:
ideal would be still have ability to sort and filter by status of given type
class Task {
// this is list of all statuses
protected $statusHistory;
protected $lastStatus;
public function __construct() {
$this->statusHistory = new Arraycollection();
}
public function addStatus($status) {
$this->statusHistory[] = $status;
$this->lastStatus = $status;
}
public function getStatusHistory() {
return $this->statusHistory;
}
public function getLastStatus() {
return $this->lastStatus;
}
}
// to get list of statuses
$task->getStatusHistory();
// to get last status, it returns Status object, not collection
$task->getLastStatus();
It's more or less standard approach when you need first/last element from collection and getting the whole collection might be an overhead.