How to delete/debug multiple rows with Doctrine? - php

I have a Foos entity associated to foos table.
I'm trying to delete many rows from there using createQueryBuilder().
I am failing with that task, and nothing is logged, no exception, and no DELETE queries sent to mysql (I tried to log all queries in mysql).
Am I missing something?
/** #var Doctrine\ORM\EntityManagerInterface $this->entityManager */
$qb = $this->entityManager->createQueryBuilder()
->delete('Foos', 'foo')
->where('foo.some_column = :someAttr and another_column != :anotherAttr')
->setParameter('someAttr', $someAttr)
->setParameter('anotherAttr', $anotherAttr);
I inspected the getDQL() and the parts and query seems to be correct. But the test rows are not being deleted as expected.
How can I debug or make it right?

You are not executing the query.
$qb, in your example, is just a QueryBuilder, as evidenced by the call to createQueryBuilder().
After you are done building the query, you need to get the built object and execute it:
$qb->getQuery()->execute();

Related

Why query result suddenly changed after called from other medthod in Laravel

I have problem here with query result from Eloquent, I tried to query from DB and put in variable $contractList in my mount() method and the result as expected. But when I tried to retrieve specific data from $contractList with $contractList->find($id), the result not same as in mount() method.
Here is query from mount():
public function mount(){
$contractList = Kontrak::select(['id', 'mou_id'])->with(['order:id,kontrak_id', 'order.worklist:id', 'order.worklist.khs:id,mou_id,worklist_id,khs', 'amdNilai:id,kontrak_id,tgl_surat'])->withCount('amdNilai')->get()
}
Here the result:
But when I tried to find specific data from $contractList, properties that shown not same as in mount:
public function itemSelected($id)
{
//amd_nilai_count not showing
$kontrak = $this->contractList->find($id);
if ($kontrak->amd_nilai_count == 1) {
$this->nilai_amd = $this->calculateNilai($id);
}
}
Here is the result called from itemSelected():
I have tried use get() but the result still problem, how to get same properties same as in mount().By the way im use laravel & livewire.
As i read your comments you seem to mix up ActiveRecords ORM with an Data Mapper ORM. Laravel uses active records.
Laravel will always fetch models on every data operation.
Kontrak::select('name')->find(1); // select name from kontraks where id = 1;
Kontrak::find(1); // select * from kontraks where id = 1;
This will always execute two SQL calls no matter what and the objects on the heap will not be the same. If you worked with Doctrine or similar, this would be different.
To combat this you would often put your logic in services or similar.
class KontrakService
{
public function find(int $id) {
return Kontrak::select(['id', 'mou_id'])->find($id);
}
}
Whenever you want the same logic, use that service.
resolve(KontrakService::class)->find(1);
However, many relationship operations is hard to do with this and then it is fine to just fetch the model with all the attributes.

Eloquent: where are queries executed

Atm I am building a very specific solution for an existing application written in Laravel. The solution executes queries in c++ modifies data, does sorting and returns the results. This c++ program is loaded in via a PHP extension and serves a single method to handle this logic.
The method provided by the extension should be implemented in Laravel using Eloquent, I've been looking at the source code for ages to find the specific method(s) that execute the queries build with Eloquensts Builder.
Where can I find the methods that actually perform the queries?
Why c++? I hear you think. The queries should be executed on multiple schemas (and/or databases) over multiple threads for improved performance. Atm 100+ schemas are being used with each containing thousands of records per table.
After a lot of troubleshooting and testing I have found a solution to my problem. In the class Illuminate\Database\Query\Builder you can find a method called runSelect(). This method runs a select statement against the given connection and returns the selected rows as an array.
/**
* Run the query as a "select" statement against the connection.
*
* #return array
*/
protected function runSelect()
{
return $this->connection->select(
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
);
}
What I did to test my implementation in c++ to run the selects, I mapped the return values of $this->getBindings() to a new array to do some necessary modifications to some strings and did a simple str_replace_array on the prepared statement to get the full query. Eventually the c++ program will execute the prepared statemend and not the parsed query.
The modified method to suit my case looks like this. This has been done quick and dirty for now to test if it is possible, but you get the idea. Works as a charm except for the count() method in eloquent.
/**
* Run the query as a "select" statement against the connection.
*
* #return array
*/
protected function runSelect()
{
$bindings = [];
foreach ($this->getBindings() as $key => $value) {
$bindings[] = $value; // Some other logic to manipulate strings will be added.
}
$query = str_replace_array('?', $bindings, $this->toSql());
$schemas = ['schema1', 'schema2', 'schema3', 'schema4']; // Will be fetched from master DB.
return runSelectCpp($schemas, $query);
}

ORMInvalidArgumentException: A new entity was found through the relationship For multiple iterations

I am getting doctrine error as explained below. I checked the solutions given in other questions but it's not something I am looking for.
So basically i have an action for generating exam cards as(just putting relevant code)-
function generateExamCardAction{
$examSetting = $this->getExamSetting();
$this->insertExamCard($classId, $examSetting);
$insertedExamCard = $this->fetchExamCard($classId);
$this->generatePdf($insertedExamCard);
}
And the insertExamCard function goes like this:
function insertExamCard($classId, $examSetting){
$em = $this->getEntityManager();
$examCard = new ExamCard();
$examCard->setClassId($classId);
$examCard->setExamSetting($examSetting);
$em->persist($examCard);
$em->flush();
$em->clear();
}
And the doctrine association between ExamCard and ExamSetting is as:
/**
* #ORM\ManyToOne(targetEntity="ExamSetting")
* #ORM\JoinColumn(name="ExamSettingId", referencedColumnName="id")
*/
protected $examSetting;
Please note that i have not cascaded it using cascade={"persist"} beacuse i dont want to manipulate ExamSetting table at any cost as it's master table.
Till here everything is working fine. ExamCard is getting inserted properly, then fetched properly and then generating pdf properly.
But now I have to iterate the logic in generateExamCardAction() for multiple classes.
So it would be like this:
function generateExamCardAction{
$examSetting = $this->getExamSetting();
foreach($classes as $classId){
$this->insertExamCard($classId, $examSetting);
$insertedExamCard = $this->fetchExamCard($classId);
$this->generatePdf($insertedExamCard);
}
}
In this case the the first iteration is working fine. That is for 1st class it's inserting ExamCard, then fetching and generating pdf properly.
But for second iteration it's giving below error:
Doctrine\ORM\ORMInvalidArgumentException: A new entity was found
through the relationship
'Application\Entity\ExamCard#examSetting' that was not
configured to cascade persist operations for entity
I believe the problem is with $em->clear().
I tried by changing it to $em->clear($examCard). In that case it doesn't give error but the data fetched by fetchExamCard() doesn't have ExamSetting fields set in ExamCard.
Could you please help me find why it's working for single iteration but not for multiple iterations?

what is difference between where and find in laravel

when I write this $thread = Thread::find($id); then I write {{$thread->title}} it gives me the title of the thread, but when I write $thread = Thread::where('id','=',$id); then I write {{$thread->title}} it gives me an error.why is that happening?
You should write:
$thread = Thread::where('id','=',$id)->first();
to get one column, else laravel will understand it as array.
You need to call the get() (or any of its variants) method to execute the actual query when using where.
Thread::where('id','=',$id)->get();
Otherwise Thread::where('id','=',$id) just gets you an instance of eloquent's query builder.
find() on the other hand will automatically run a query for whatever it is you want to find by you can't do all sorts of useful stuff (e.g. orderBy, paginate, etc.) that you can very easily pull of using the query builder.

Memcache with Symfony/Doctrine is reloading data

In my symfony project, I have a "complex" query that looks like:
$d = Doctrine_Core::getTable('MAIN_TABLE')
// Create the base query with some keywords
->luceneSearch($keywords)
->innerJoin('w.T1 ws')
->innerJoin('ws.T2 s')
->innerJoin('w.T3 piv')
->innerJoin('piv.T4 per')
->innerJoin('w.T5 st')
...
->innerJoin('doc.T12 docT')
->innerJoin('w.Lang lng')
->execute();
I added all those innerJoin to reduce the number of query due to my data model. Actually all data are recovered with this only query.... but the query took from 2 to 20 sec. depends on keywords.
I decided to use memcache because data are not changing all the time.
What I've done is configuring memcache and adding
...
->useResultCache(true)
->execute();
to my query.
What is strange is that :
The first time (when the cache is empty/flushed), only one query is execute
The second time, ~130 ares executed and it take more time than the first...
Those "new" queries are retrieving data from "inner join" for each record.
What I don't undestand is why "innerjoined" data are not saved in the cache?
I tried to change the hydrate mode but it seems not to be influent.
Someone has an idea?
After a whole day to googlise, to analyse doctrine and become desperate, I found an article that explain the solution:
class User extends BaseUser{
public function serializeReferences($bool=null)
{
return true;
}
}
The problem was the profile object was not getting stored in the result cache and thus causing a query each time it was called from the user object. After much hunting around, a long time in #doctrine, and a few leads from a couple of people, it turns out, by default, Doctrine will only serialize the immediate relation to the main object. However, you can make it so that it will serialize objects further down the line by overriding the function serializeReferences to return true in the class you want to serialize references from. In my example this is the User class. Since our application will never only need the ‘User’ class to be serialized on a result cache I completely overrode the function and made it always return true
http://shout.setfive.com/2010/04/28/using-doctrine-result-cache-with-two-deep-relations/

Categories