How to save survey progress for a user - symfony

hi i'm doing a survey i could record the progress of the quiz according to the user
example: user is at question 15/50 he must be able to disconnect and continue where he was stopping.
I manage to assign the answers to the user, but not to worry about the progress. thank you so much
so here I got my answer via the post method
my controlleur to save answer for a user..
class DefaultController extends Controller
{
/**
* #Route("/Reponse/thematique", name="thematique_reponse")
* #Method({"GET", "POST"})
*/
public function reponseThematique(Request $request)
{
//instance des repository
$userSlpRepo = $this->getDoctrine()->getRepository(UserSlp::class);
$reponseThematiqueRepo = $this->getDoctrine()->getRepository(Reponse_thematique::class);
$questionMangerRepo = $this->getDoctrine()->getRepository(Manger::class);//here all the questions
$em = $this->getDoctrine()->getManager();
$userSlp = $userSlpRepo->findOneByGaeaUserId($this->getUser()->getId());
$datas = $request->request->all();
foreach ($datas as $data => $value ){
$question = $questionMangerRepo->find($data);
$answer = new Reponse_thematique;
$answer->setManger($question);
$answer->setValue($value);
$answer->setUserSlp($userSlp);
$em->persist($answer);
$em->flush();
}
return new response('ok');
}
I thought to make a relationship with an entity "questionnaire-progress" for example or we will have an id, question_id, user_id and why not a column or we would put a boolean if the questionaire is finished or not.
/**
* SurveyProgress
*
* #ORM\Table(name="survey_progress", indexes={#ORM\Index(name="IDX_7EF6B461B3FE509D",
* columns={"questionnaire_id"}), #ORM\Index(name="IDX_7EF6B4611E27F6BF",
* columns={"manger_id"}), #ORM\Index(name="IDX_7EF6B461FDDFEACC",
* columns={"userSlp_id"})})
* #ORM\Entity
*/
class QuestionnaireProgress
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var bool
*
* #ORM\Column(name="done", type="boolean", nullable=false)
*/
private $done;
/**
* #var \Question
*
* #ORM\ManyToOne(targetEntity="manger")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="question_id", referencedColumnName="id")
* })
*/
private $manger;
/**
* #var \Survey
*
* #ORM\ManyToOne(targetEntity="questionnaire")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="questionnaire_id", referencedColumnName="id")
* })
*/
private $questionnaire;
/**
* #var \UserSlp
*
* #ORM\ManyToOne(targetEntity="UserSlp")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="userSlp_id", referencedColumnName="id")
* })
*/
private $userslp;
}

You could save the answers everytime the user answers. Then, you'll have this information stored.
I suppose a Survey contains a list of Questions. You can then get the total number of questions.
If you save the answers when the user answers, the number of Reponse_thematique for a given UserSlp and a given Survey / the total number of questions will give you the progress

Related

Persist create new entity while already exists (Ont-To-Many Many-To-One relation)

I have an Angular4 app working with Symfony3/Doctrine2 Rest Api.
Both in Angular and Symfony, I have those entities :
Table
TableNode
Node
The relation between Table and Node is :
Table (OneToMany) TableNode (ManyToOne) Node
What is a "ManyToMany" relation with attributes.
In the Angular app, I create a new Table (form a TableModel that has exactly the same properties that the Table entity in the Symfony app).
This Table contains several Node entities that come from the Api (so they already exists in my database).
What I want is to create a new Table that contains new TableNode entities and each TableNode should contain existing Node entities.
When I want to save my table within the db, I call my Api through a Put action :
/**
* PUT Route annotation
* #Put("/tables")
*/
public function putTableAction(Request $request)
{
$em = $this->getDoctrine()->getManager('psi_db');
$serializer = $this->container->get('jms_serializer');
$dataJson = $request->query->get('table');
$table = $serializer->deserialize($dataJson, Table::class, 'json');
// Here, my $table has no id (that's ok), the TableNode subentity has no id (ok) and my Node subentity already have an id (because they come from the db)
$em->persist($table);
// Here, my $table has a new id (ok), my TableNode has a new id (ok) BUT my Node subentity have a NEW id, so it will be duplicated
$em->flush();
$view = $this->view();
$view->setData($table);
return $this->handleView($view);
}
I tried to use $em->merge($table) instead of $em->persist($table) and my node subentities keep there own id (so they may not be duplicated within the flush) BUT the table and tableNode have no id (null) and are not persisted.
The only solution I found is to loop through the TableNode entities, retrieve the Node entity from the database and do a tableNode->setNode :
$tns = $table->getTableNodes();
foreach ($tns as $tn) {
$nodeId = $tn->getNode()->getId();
$dbNode = $nodeRepo->find($nodeId);
$tn->setNode($dbNode);
}
But it's not a good solution because I make a db search within a loop and a table could contains more than a hundred of TableNode/Node so it might take a lot of resources.
Does anyone have a cleaner solution ?
Thanks.
edit : add classes
Table :
/**
* Table_
* Doctrine "Table" is a reserved name, so we call it Table_
*
* #ORM\Table(name="psi_table")
* #ORM\Entity(repositoryClass="AppBundle\Repository\Table_Repository")
*
* #ExclusionPolicy("all")
*/
class Table_
{
public function __construct()
{
$this->tNodes = new ArrayCollection();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*
* #Expose
*/
private $name;
/**
* #var \stdClass
*
* #ORM\Column(name="author", type="object", nullable=true)
*
* #Expose
*/
private $author;
/**
* #var \stdClass
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\TableNode", mappedBy="table", cascade={"persist"})
*
* #Expose
* #Type("ArrayCollection<AppBundle\Entity\TableNode>")
* #SerializedName("tNodes")
*/
private $tNodes;
}
TableNode :
/**
* TableNode
*
* #ORM\Table(name="psi_table_node")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TableNodeRepository")
*
* #ExclusionPolicy("all")
*/
class TableNode
{
public function __construct($table = null, $node = null, $position = null)
{
if($table) $this->table = $table;
if($node) $this->node = $node;
if($position) $this->position = $position;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer")
*
* #Expose
*/
private $position;
/**
* #var string
*
* #ORM\Column(name="groupSocio", type="string", nullable=true)
*
* #Expose
* #SerializedName("groupSocio")
*/
private $groupSocio;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Table_", inversedBy="tNodes", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Table_")
*/
private $table;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Node", inversedBy="tables", cascade={"persist", "merge"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Node")
*/
private $node;
}
Node :
/**
* TableNode
*
* #ORM\Table(name="psi_table_node")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TableNodeRepository")
*
* #ExclusionPolicy("all")
*/
class TableNode
{
public function __construct($table = null, $node = null, $position = null)
{
if($table) $this->table = $table;
if($node) $this->node = $node;
if($position) $this->position = $position;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer")
*
* #Expose
*/
private $position;
/**
* #var string
*
* #ORM\Column(name="groupSocio", type="string", nullable=true)
*
* #Expose
* #SerializedName("groupSocio")
*/
private $groupSocio;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Table_", inversedBy="tNodes", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Table_")
*/
private $table;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Node", inversedBy="tables", cascade={"persist", "merge"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Node")
*/
private $node;
}
Submitted data (example) :
{"tNodes":[{"id":0,"position":0,"groupSocio":"group1","node":{"id":683,"frontId":"1502726228584","level":"synusy","repository":"baseveg","name":"A synusy from my Angular app!","geoJson":{"type":"FeatureCollection","features":[{"type":"Feature","properties":[],"geometry":{"type":"Point","coordinates":[-10.0634765625,42.0982224112]}}]},"lft":1,"lvl":0,"rgt":2,"children":[{"id":684,"frontId":"1502726228586","level":"idiotaxon","repository":"baseflor","name":"poa annua","coef":"1","geoJson":{"type":"FeatureCollection","features":[{"type":"Feature","properties":[],"geometry":{"type":"Point","coordinates":[-10.0634765625,42.0982224112]}}]},"lft":1,"lvl":0,"rgt":2,"validations":[{"id":171,"repository":"baseflor","repositoryIdTaxo":"7075","repositoryIdNomen":"50284","inputName":"poa annua","validatedName":"Poa annua L."}]}],"validations":[]}}]}
The purpose putTableAction is to:
create new instances of Table_
create new instance of TableNode
do nothing with Node
It means that:
1.You do not need to submit any details of Node. Id field is enough:
{"tNodes":[{"id":0,"position":0,"groupSocio":"group1","nodeId": 683}]}
2.You can add one more field to TableNode, called $nodeId, and map it with "node" field in DB. The purpose of this field is to simplify deserialization, in all other places you can use $node field.
/**
* #var integer
*
* #ORM\Column(name="node", type="integer")
*
* #Expose
*/
private $nodeId;

Symfony entity validation verify foreign key exists

I would like to be able to use entity validator constraints to verify if the foreign key book_id is valid, please see below:
Book.php
/**
* Book
*
* #ORM\Table("book")
* #ORM\Entity
* #ORM\Entity(repositoryClass="AppBundle\Repository\BookRepository")
*/
class Book
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #ORM\Column(name="name", type="string")
* #Assert\Length(
* max = 250,
* maxMessage = "Name cannot be longer than {{ limit }} characters",
* groups={"create","update"}
* )
*/
private $name;
/**
* #ORM\OneToOne(targetEntity="Loan", mappedBy="book", fetch="LAZY")
*/
protected $loan;
}
Loan.php
/**
* Loan
*
* #ORM\Table("loan")
* #ORM\Entity
* #ORM\Entity(repositoryClass="AppBundle\Repository\LoanRepository")
*/
class Loan
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
* #ORM\Column(name="book_id", type="integer")
*/
protected $book_id;
/**
* #var string
* #ORM\Column(name="name", type="string")
* #Assert\Length(
* max = 500,
* maxMessage = "Person cannot be longer than {{ limit }} characters",
* groups={"create","update"}
* )
*/
private $person;
/**
* #ORM\OneToOne(targetEntity="Book", inversedBy="loan")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $book;
}
Here is how I am currently validating the loan entity
$loan = new Loan();
$loan->setPerson($person);
$loan->setBookId($id);
/** #var ConstraintViolation $error */
foreach ($this->get('validator')->validate($loan,null,['create'])->getIterator() as $index => $error) {
$errorMessages[] = $error->getMessage();
}
I figured maybe I can add a custom validator like this to the loan entity:
/**
* #Assert\IsTrue(message = "The book does not exist")
* #return bool
*/
public function isBookLegal(BookRepository $bookRepository)
{
return !$bookRepository->fetchById($this->book_id);
}
But I end up with the follow exception:
Type error: Too few arguments to function
AppBundle\Entity\Loan::isBookLegal(), 0 passed and exactly 1 expected
First of all, you should not have both $book_id and $book in your Loan entity. You should remove $book_id, which is enough for your entity relationship.
Then, all you need to do is add an #Assert\NotBlank() on $book:
use Symfony\Component\Validator\Constraints as Assert;
...
/**
* #ORM\OneToOne(targetEntity="Book", inversedBy="loan")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $book;
I'm not sure what code you are using to get all of your loans, but as Edwin states that's not really good form. You want want something more like:
foreach ($loans as $loan) {
$errors = $this->get('validator')->validate($loan);
// do something here if there is an error
}
The assertion function you wrote isn't going to work because you can't pass in a value to your isBookLegal() function there, nor can you ever use the database connection/repository from within your Entity class.
I'm not really sure what you are trying to accomplish without greater context here. Doctrine is already going to validate your $book member because of your #ORM\OneToOne annotation in the first place. You don't have to perform any additional validation. I'm guessing you are trying to pass in a value directly to $book_id, which is incorrect. You should only be passing already-valid $book entities to your Loan class, via $loan->setBook(Book $book);

Embeed forms - inserting to database

I created two entities automatically ( using this manual http://symfony.com/doc/2.8/doctrine/reverse_engineering.html) based on ER model generated in Workbench. My intention was to create one-to-one relationship but annotation show it is one-to-many relationship. I created also embeed forms. I would like to insert client and new adress to database. I still get an error:
A new entity was found through the relationship 'UlaBundle\Entity\Client#adres' that was not configured to cascade persist operations for entity: qqq. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
Error is shown even if i set #ManyToOne(..,cascade={"persist"}) and __toString function. What is the problem? Please help. Below my code:
///Client Entity
class Client
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \UlaBundle\Entity\Adres
*
* #ORM\ManyToOne(targetEntity="UlaBundle\Entity\Adres", cascade= {"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="adres_id", referencedColumnName="id")
* })
*/
private $adres;
/// Adres Entity
class Adres
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
///Controller
/**
* #Route("/client", name="client")
*/
public function clientAction(Request $request) {
$c = new Client();
$form = $this->createForm(ClientType::class,$c);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$m = $this->getDoctrine()->getManager();
$m->persist($c);
$m->flush();
return new Response('Added');
}
return $this->render('UlaBundle:Default:client_form.html.twig', array('form' => $form->createView()));
}
I think your problem come from the blank space in cascade= {"persist"}, you should remove it
/**
* #var \UlaBundle\Entity\Adres
*
* #ORM\ManyToOne(targetEntity="UlaBundle\Entity\Adres", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="adres_id", referencedColumnName="id")
* })
*/
private $adres;

Doctrine 2 - ManyToOne association not working

I need a hand with the following code.
I have this two clases:
Class Expert{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="username", type="string", length=255)
* #Assert\NotBlank()
*/
protected $username;
/**
* #ORM\Column(name="email", type="string", length=255, unique=true)
* #Assert\NotBlank()
*/
protected $email;
/**
* #ORM\Column(name="password", type="string", length=40)
* #Assert\NotBlank()
*/
protected $password;
}
Class Job{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="titulo", type="string", length=255)
* #Assert\NotBlank()
*/
protected $title;
/**
* #ORM\Column(name="description", type="text")
* #Assert\NotBlank()
*/
protected $description;
/**
* #ORM\ManyToOne(targetEntity="Expert")
* #ORM\JoinColumn(name="expert_id", referencedColumnName="id")
*/
protected $assigned_expert;
}
And this custom Repository:
class JobRepository extends EntityRepository
{
public function getTechnicianFinishedJobs($id)
{
$Q = $this->getQueryBuilder('j')
->where('j.expert = :expert_id')
->setParameter('expert_id', $id)
->getQuery()
try{
return $q->getResult();
}catch(NoResultException $e){
return false;
}
}
}
When I run this I get the following error:
[Semantical Error] line 0, col 68 near 'expert = :e': Error: Class Job has no field or association named expert
The idea is that one expert can be assigned to many jobs and one job can be assigned to one expert . The job needs to know who's the designated expert but not the other way around, so that's why I use a ManyToOne unidirectional association.
I tried changing the repository to ->where('j.expert_id = :expert_id') and other combinations with no avail.
Can somebody tell me what I'm doing wrong?
Thanks in advance.
If 'j' is your job table, you can't use j.expert, because this is (as far as I can tell) no attribute of your table/entity. You named the field 'expert_id'.
I guess it should be:
$Q = $this->getQueryBuilder('j')
->where('j.assigned_expert = :expert_id')
->setParameter('expert_id', $id)
->getQuery()
#alvk4: He explained why he didn't use bidirectional association. What you suggested, forgive me if I'm wrong, is bidirectional association.
You miss something on your annotation see an working example:
/**
* Fluency\Bundle\DesktopBundle\Entity\Application
*
* #ORM\Table(
* name="desktop.applications",
* uniqueConstraints={
* #ORM\UniqueConstraint(name="applications_jsid_key", columns={"jsid"}),
* #ORM\UniqueConstraint(name="applications_type_key", columns={"type"}),
* #ORM\UniqueConstraint(name="applications_classname_key", columns={"classname"})
* }
* )
* #ORM\Entity(repositoryClass="Fluency\Bundle\DesktopBundle\Entity\Repository\ApplicationRepository")
*/
class Application
{
...
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Fluency\Bundle\DesktopBundle\Entity\ApplicationFile", mappedBy="application", cascade={"persist"})
* #ORM\JoinTable(name="desktop.application_files",
* joinColumns={
* #ORM\JoinColumn(name="idapplication", referencedColumnName="idapplication")
* }
* )
*/
private $files;
...
}
/**
* Fluency\Bundle\DesktopBundle\Entity\ApplicationFile
*
* #ORM\Table(name="desktop.application_files")
* #ORM\Entity
*/
class ApplicationFile
{
...
/**
* #var \Fluency\Bundle\DesktopBundle\Entity\Application
*
* #ORM\ManyToOne(targetEntity="Application", inversedBy="files")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="idapplication", referencedColumnName="idapplication", onDelete="CASCADE")
* })
*/
private $application;
...
}
See a working example of DQL on my repository class:
...
public function getApplicationFilesByJsid($jsid)
{
if(empty($jsid) OR !$jsid OR !is_string($jsid))
{
throw new \Psr\Log\InvalidArgumentException();
}
$query = $this->getEntityManager()->createQueryBuilder()
->select('a, af, m, ft')
->from($this->getEntityName(), 'a')
->innerJoin('a.files', 'af')
->innerJoin('a.module', 'm')
->innerJoin('af.filetype', 'ft')
->where('a.active = 1 AND a.jsid = :jsid')
->setParameter('jsid', $jsid)
->orderBy('af.id', 'ASC')
->getQuery();
$applicationFiles = $query->getSingleResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
return $applicationFiles;
}
...
#enigma: Yes is bidirectional, but your DQL its'n right, would be j.assigned_expert, but anyway the Expert is owning side of relationship, also he needs set mappedBy=assigned_expert on annotation.

Doctrine2 not update DateTime [duplicate]

This question already has answers here:
Doctrine2 ORM does not save changes to a DateTime field
(3 answers)
Closed 8 years ago.
I think I have found a bug which I can not find solution ..
I try to update the datetime field, but do not update it, don't gives me an error.
Move all other fields modifies them correctly, but the datetime field no.
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('MyOwnBundle:Events')->find($id);
$In = $entity->getDateIn();
$In->modify('+1 day');
$entity->setDateIn($In);
$em->flush();
I also tried to insert a DateTime() object directly but does not update at all!
$entity->setDateIn(new \DateTime());
Is there a solution to this problem?
I installed symfony 2.1 and doctrine 2.3.3
EDIT
Event entity:
/**
* Events
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="My\OwnBundle\Entity\EventsRepository")
*/
class Events
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var \DateTime
*
* #ORM\Column(name="dateIn", type="datetime")
*/
private $dateIn;
/**
* #var \DateTime
*
* #ORM\Column(name="dateOut", type="datetime")
*/
private $dateOut;
....
/**
* Set dateIn
*
* #param \DateTime $dateIn
* #return Events
*/
public function setDateIn($dateIn)
{
$this->dateIn = $dateIn;
return $this;
}
/**
* Get dateIn
*
* #return \DateTime
*/
public function getDateIn()
{
return $this->dateIn;
}
/**
* Set dateOut
*
* #param \DateTime $dateOut
* #return Events
*/
public function setDateOut($dateOut)
{
$this->dateOut = $dateOut;
return $this;
}
/**
* Get dateOut
*
* #return \DateTime
*/
public function getDateOut()
{
return $this->dateOut;
}
....
The modify() method will not update the entity since Doctrine tracks DateTime objects by reference. You need to clone your existing DateTime object, giving it a new reference. Modify the new one and then set is as a new timestamp.
For more information, see the article in the Doctrine Documentation.
the entity is right, but you need to persist your entity with $em->persist($entity) and you don't need to set again the date because the datetime is passed by reference
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('MyOwnBundle:Events')->find($id);
$entity->getDateIn()->modify('+1 day');
$em->persist($entity);
$em->flush();

Resources