Get auto generated ID right after flush() Symfony3 - php

I have a Project entity and when creating a new project I need to be able to upload files. For this reason I have created Files entity which saves the path and projectID. My problem is that I don't know how to retrieve it after creating it. Here is what I am trying to do
$projectService->createProject($project,$user,$isWithoutTerm,self::NO_TERM_DEFAULT_VALUE);
$filesService = $this->get('app.service.files_service');
foreach ($managerFiles as $managerFile){
$fileName = $filesService->uploadFileAndReturnName($managerFile);
$filesService->createFile($fileName,$project->getId(),$user);
}
Currently my $project doesn't have ID which means that I cant create a new file. I heard that I could use $em->retrieve(object), but the actual flushing is not done in the controller. If i try to use it in my createProject function and return it for some reason I can't. PHPStorm says that it is a void function. Here is the code in createProject
$project->setFromUser($user->getFullName());
$project->setDepartment($user->getDepartment());
$project->setIsOver(false);
$project->setDate(new \DateTime());
if($isWithoutTerm){
$project->setTerm(\DateTime::createFromFormat('Y-m-d', $noTermDefaultValue));
}
$this->entityManager->persist($project);
$this->entityManager->flush();
Is there a way to retrieve the projectID after flushing and being able to use it in my controller?

It could be a race condition where the $project is not available yet and so the service cannot load it and thus getId() fails.
First, create your project entity:
//build your $project
$this->entityManager->flush();
Second, send this $project to your "service"
$projectService->createProject($project,$user,$isWithoutTerm,self::NO_TERM_DEFAULT_VALUE);
$filesService = $this->get('app.service.files_service');
foreach ($managerFiles as $managerFile){
$fileName = $filesService->uploadFileAndReturnName($managerFile);
$filesService->createFile($fileName,$project->getId(),$user);
}
More specifically if you wrap this in a try/catch you can identify exactly what is happening:
try {
$project = new Project();
//build project
$this->entityManager->flush();
//load $projectService..
$projectService->createProject($project,$user,$isWithoutTerm,self::NO_TERM_DEFAULT_VALUE);
$filesService = $this->get('app.service.files_service');
foreach ($managerFiles as $managerFile){
$fileName = $filesService->uploadFileAndReturnName($managerFile);
$filesService->createFile($fileName,$project->getId(),$user);
}
}
catch(\Exception $e) {
//$e->getMessage() will tell you if you're good to go, or if there
//is actually an issue
}
However, it could also be as simple as PHPStorm not being able to introspect your getId() method and thus doesn't understand it. OR getId() is not actually returning the id.

Upgrading the Symfony from 3.2 and 3.3 fixed the issue. I don't think it was due to the version itself, but rather removing depricated stuff and changing a lot of things and something fixed it.

Related

Make model dynamically using user supplied model name in laravel

I am stuck with it and i couldn't find any appropriate solution for this. what i want to achieve is in my admin panel based upon check box value i want to change active status of specific control and update database value using ajax. I want to make a common ajax function in controller to avoid repeatedly writing same ajax function for other controls like menu manager content manager, documents manager etc.So i want to send Model name to the ajax controller so that same function can be used. Ajax call is working perfectly but couldn't make appropriate models. for example:
$m = new App\Model.$request->model OR $m = 'App\Model\'.$request->model (adding last \ gives an error) or answer provided in Dynamically use model in laravel is not helping either. Is there any better ways if yes please suggest. I can do this below but it is hardcoded so i want to make model dynamic models
if($request->model ==='Menu')
$model = new \App\Http\Models\Menu;
else if($request->model === 'News')
$this->model = new \App\Http\Models\News;
else if($request->model === 'Document')
$this->model = new \App\Http\Models\Document;
Thankyou !!!
You can just use:
$modelName = '\\App\\Http\\Models\\'.$request->model;
$this->model = new $modelName;
However you should add validation to make sure only some allowed models would be used or at least do something like this:
if (! in_array($request->model, ['Menu', 'News', 'Document']))
{
throw new \Exception('Invalid model');
}
$modelName = '\\App\\Http\\Models\\'.$request->model;
$this->model = new $modelName;
This is because you don't want to expose probably all the models data for security reasons.
Try the below code
if($request->model){
$m = '\App'. '\' .$request->model;
//other code
}

Since Laravel 5.3 update, my views are not refreshed properly when I update data in my DB

A really strange issue here. I had a Laravel 5.2 application which work perfectly. Then I update to Laravel 5.3 to use the new broadcasting features and I face a big issue.
When I update the data (with my application forms or directly in my database) the views are note updated properly. I try to clear cache, views and config but nothing change... I need to go to some others pages and the data finish by appear...
I have a Campaign model and a page which list campaigns. When I remove an entry directly in the database, the list doesn't change in front. Also when I use debugging functions like dd results tell me that data haven't changed...
Is there someone else which faced the same problem ?
I've followed the migration guide to update my 5.2 to 5.3, maybe I forgot something...
Here a piece of my .env file :
DB_CONNECTION=mysql
BROADCAST_DRIVER=redis
CACHE_DRIVER=array
SESSION_DRIVER=file
QUEUE_DRIVER=database
Thanks !
Thank you for sharing this questions.
Laravel successfully upgraded to version 5.3 and there are some deprecations and application service provider and also some new feature like passport are added.
Your problem is with view. As per my knowledge, you need to remove arguments from your "boot" method which are written in EventServiceProvider, RouteServiceProvider, AuthServiceProvider which are available on app/provider/remove_the_arguments_from_boot_method_given_file
In Laravel 5.2:
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
}
But in Laravel 5.3:
public function boot()
{
parent::boot();
}
Kindly refer Laravel 5.3 docs
I hope, this works for you.
Thanks and regards.
Never store full model in session, it can lead to old data displayed in the application !
After a day of search and refactoring I found what was my original problem !
It's a simple session() statement which cause my application to display invalid data.
History
The dashboard display a list of campaigns which are linked to a client. A user can manage multiple clients so I put the current client in session to know which one is currently used.
The mistake here is that I put the entire client model in session so when I read the session and retrieve data, all the relationships are retrieved too.
The client is the central point to access data in my application. I retrieve the campaigns linked to my client and everything is related to it.
Here the vicious function :
/**
* Retrieve the current client instance when the user is connected
* #return App\Client|null
*/
protected function retrieveCurrentClient()
{
$client = null;
if (Gate::allows('manage-clients')) {
if (null === $client = session('currentClient')) {
$client = Client::all()->first();
session(['currentClient' => $client]);
}
} elseif (Auth::guard()->check()) {
$client = Auth::guard()->user()->client;
}
return $client;
}
In fact the problem appeared when I dig around the Gate definition. If I remove them my application starts working again...
Solution
I just change the function to store in session client id instead of the full Model. Then I retrieve fresh data in each page of my application.
/**
* Retrieve the current client instance when the user is connected
* #return App\Client|null
*/
protected function retrieveCurrentClient()
{
$client = null;
if (Gate::allows('manage-clients')) {
if (null === $client_id = session('client_id')) {
$client = Client::all()->first();
session(['client_id' => $client->id]);
} else {
$client = Client::findOrFail($client_id);
}
} elseif (Auth::guard()->check()) {
$client = Auth::guard()->user()->client;
}
return $client;
}
Don't know if it can help someone else to avoid that mistakes but happy to have found an answer !

Symfony/Doctrine not updating in memory

I suspect this is a straightforward problem about entities not updating in memory.
Here are my repository functions:
public function insertTask($information)
{
...
$entityManager = $this->getEntityManager();
$entityManager->persist($taskObj);
$entityManager->flush();
// Update the task defects for this task
$this->updateTaskDefectTaskIds($taskObj->getId(), $task['defects']);
return $taskObj;
}
private function updateTaskDefectTaskIds($task, $defects)
{
$taskDefectIds = array();
foreach ($defects as $defect)
{
$taskDefectIds[] = intval($defect['taskDefectId']);
}
// Update the task IDs on the task defects
$this->getEntityManager()
->createQuery('UPDATE Model:TaskDefect td SET td.task = :task WHERE td.id IN (:taskDefectIds)')
->setParameter('task', $task)
->setParameter('taskDefectIds', $taskDefectIds)
->execute();
$this->getEntityManager()->flush();
}
This appears to be working. Basically I have:
- Task table
- TaskDefect table
TaskDefects by virtue of the way the app is written, get inserted before the Task does. But this means I need to, once the Task is eventually added, update the TaskDefect records to point to the new TaskId.
The problem I face though, is somewhere in memory (from the actual database side of things it is updating correctly) it's not picking up my changes. For example, I update some TaskDefects to point to the new TaskId - but then I access the Task object and it says there are no defects.
If I go to another page, and try and access the same task - then it says there are defects.
So I feel I'm missing a flush() or a persist() or something which is stopping the entities in memory from updating. Obviously reloading a page forces the refresh and it works fine then.
Here's what I have in my controller:
$task = $repository->insertTask($content); // i figured maybe at this point it's too much to expect the task obj to magically update
$updatedTask = $repository->findOneById($task->getId()); // so I grab it again... but no luck
var_dump($updatedTask->getDefects()); // ... because this returns no defects
Any ideas welcome. Thanks.
This line in insertTask:
return $taskObj;
Has no connection to what is happening in updateTaskDefectTaskIds because you're not modifying $taskObj at all, you're just passing an id value, then updating defect objects via DQL.
If you'd like $taskObj to reflect your defect additions from insertTask you would do something like this:
public function insertTask($information)
{
...
$entityManager = $this->getEntityManager();
$entityManager->persist($taskObj);
$entityManager->flush();
// Update the task defects for this task
$this->updateTaskDefectTaskIds($taskObj, $task['defects']);
return $taskObj;
}
private function updateTaskDefectTaskIds($taskObj, $defects)
{
foreach ($defects as $defect)
{
$defect = $this->getEntityManager()->getRepository('YourBundle:Defect')->find(intval($defect['taskDefectId']));
if ($defect instanceof Defect) {
$defect->setTaskObj($taskObj);
$this->getEntityManager()->persist($defect);
$taskObj->addDefect($defect);
}
}
$this->getEntityManager()->persist($taskObj);
$this->getEntityManager()->flush();
}
Or, if you don't mind an extra db call just refresh $taskObj in insertTask like this:
$this->getEntityManager()->refresh($taskObj);
return $taskObj;
Also, doctrine loves to cache what you have in memory, so if it doesn't have any reason to check the db (in your code example it can't know about your change) then it will just happily serve you up the stale object when you fetch the entity by id.

For Symfony2 functional testing, what is the best practice for verifying database contents?

I see that there are a number of ways to load fixture data into a database. But after a functional test, what is the best/standard way to confirm what was written to the database was correct?
The phpunit package has a whole section for this, where you can load a dataset and then use things like assertTablesEqual() to compare a table's contents with the expected contents. But that doesn't seem to be usable under Symfony2, and I can't find any other standard method.
How do others solve this problem?
Symfony2 use doctrine ORM by default, or you can set other database gestion (MongoDB by exemple). Check the app\config\parameters.php file to set the database connection and the app\config\config.php to check/set the type of gestion. With a ORM, you do not need to check alot of stuff as the phpunit package, because it is already integrated into the protocole and much more. Check here for more details.
If you want to load datafixtures, you can export your actual database to save it, or either create a new one only for testing and switch databases in the app\config\parameters.php by create a new one like this app\config\parameters_dev.php. In this case, the website and your local version won't use the same database. You can also edit the app\config\parameters.php and prevent to upload it with the .gitgnore file.
Here is an example from a test set that includes database results. If you need to interact directly with the database in your test the entity manager can be made available to the test. For more information, see this bit of documentation. Note that results are more usually presented in a web page and read by the DOM crawler.
public function setUp()
{
self::bootKernel();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager()
;
$this->tool = static::$kernel->getContainer()
->get('truckee.toolbox')
;
$classes = array(
'Truckee\VolunteerBundle\DataFixtures\SampleData\LoadFocusSkillData',
'Truckee\VolunteerBundle\DataFixtures\SampleData\LoadMinimumData',
'Truckee\VolunteerBundle\DataFixtures\SampleData\LoadStaffUserGlenshire',
'Truckee\VolunteerBundle\DataFixtures\SampleData\LoadStaffUserMelanzane',
'Truckee\VolunteerBundle\DataFixtures\SampleData\LoadOpportunity',
'Truckee\VolunteerBundle\DataFixtures\SampleData\LoadVolunteer',
);
$this->loadFixtures($classes);
$this->client = $this->createClient();
$this->client->followRedirects();
}
public function testOutboxUser()
{
$crawler = $this->login('admin');
$link = $crawler->selectLink("Send alerts to organizations")->link();
$crawler = $this->client->click($link);
$outboxObj = $this->em->getRepository('TruckeeVolunteerBundle:AdminOutbox')->findAll();
$outbox = $outboxObj[0];
$recipient = $outbox->getRecipientId();
$type = $this->tool->getTypeFromId($recipient);
$this->assertEquals('staff', $type);
}

Symfony 1.4 save all items' froms with one action

I want to create thumbnails for 200+ objects with one action in Symfony 1.4. The problem is that thmbnail generation takes place on saving the form.
class AuthorForm extends BaseAuthorForm
{
public function configure()
{
/* some configs */
}
public function save($con = null)
{
/* create thmbnail from original picture */
}
}
How can I write an (batch) action to be able to save them all at once, rather than going to each item in the backend and saving?
Please note, that just $author->save(); won't work, of course.
Thanks!
You have to fetch the objects, loop through them, create the form and save. Like the following.
$authors = Doctrine_Core::getTable('Author')->findAll();
foreach($authors as $author){
$form = new AuthorForm($author);
$form->save();
}
You'll probably have memory issues if you're running it on a hosting plan (not your dev machine). A better way to get thumbnails is using a plugin like sfImageTransformExtraPlugin (http://www.symfony-project.org/plugins/sfImageTransformExtraPlugin) that generates a cached thumbnail as you need them. You don't even need to go through the trouble of generating the thumbnails. And still can have multiple thumbnail versions of the same photo pretty easily.
If you still need to use this way, do some unset stuff during the loop, like the following.
$authors = Doctrine_Core::getTable('Author')->findAll();
foreach($authors as $author){
$form = new AuthorForm($author);
$form->save();
unset($form, $author);
}

Categories