I will make it as simple as possible...
I have two entities : Pds and Specialite
Here are the two DbTable class :
class Application_Model_DbTable_Specialite extends Zend_Db_Table_Abstract
{
/**
* #var $_name : Nom de la table dans la BDD
* #var $_primary : Nom de la clé primaire de la table
* #var $_schema : Nom de la BDD
* #var $_adapter : Allias de la BDD dans le registre de Zend (défini dans le Bootstrap)
*/
protected $_name = 'ps_specialite';
protected $_primary = 'ps_spe_id';
protected $_schema = 'basename';
protected $_adapter = 'db_1';
protected $_referenceMap = array(
'Pds' => array(
'columns' => 'numprat_id',
'refTableClass' => 'Pds',
'refColumns' => 'id'
)
);
}
class Application_Model_DbTable_Pds extends Zend_Db_Table_Abstract
{
/**
* #var $_name : Nom de la table dans la BDD
* #var $_primary : Nom de la clé primaire de la table
* #var $_schema : Nom de la BDD
* #var $_adapter : Allias de la BDD dans le registre de Zend (défini dans le Bootstrap)
*/
protected $_name = 'ps_praticiens';
protected $_primary = 'numprat_id';
protected $_schema = 'basename';
protected $_adapter = 'db_1';
protected $_dependentTables = array('Specialite');
}
Here are the two Models :
class Application_Model_Specialite extends Zend_Db_Table_Row_Abstract {
protected $_id;
protected $_id_pds;
protected $_principal;
public function __construct(array $options = null){}
public function __set($name, $value){}
public function __get($name){}
public function setOptions(array $options){}
public function setId($id){}
public function getId(){}
public function setIdPds($id_pds){}
public function getIdPds(){}
public function setPrincipal($principal){}
public function getPrincipal(){} }
class Application_Model_Pds extends Zend_Db_Table_Row_Abstract {
protected $_id;
protected $_nom;
protected $_nom_pro;
protected $_prenom;
[ ... same contruction as Specialite ... ]
}
And my PdsMapper.php :
class Application_Model_PdsMapper { protected $_dbTable;
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Application_Model_DbTable_Pds');
}
return $this->_dbTable;
}
public function save(Application_Model_Pds $pds){}
public function find($id, Application_Model_Pds $pds)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
return;
}
$row = $result->current();
$pds->setId($row->id)
->setNom($row->nom)
->setPrenom($row->prenom);
return $pds;
}
public function fetchAll(){} }
The link between Pds and Specialite is :
Pds have one or several speciality
Specialite concerns one or several Pds
I want to get the Specialite of a PDS. Here is index action in my controller :
public function indexAction(){
$o_mapper = new Application_Model_PdsMapper();
$pds = $o_mapper->find('69000001', new Application_Model_Pds());
$pds_69000001 = $pds->current();
$specialiteByPds = $pds_69000001->findDependentRowset('Specialite');
$this->view->pds = $pds;
$this->view->specialite = $specialiteByPds;
}
But the application tell me thaht current() method is unrecognized ... I'm looking to make it work since yesterday but I don't see where is the problem...
Thanks in advance
First have a look at http://akrabat.com/zend-framework/on-models-in-a-zend-framework-application/
So you're doing several things wrongly.
First Application_Model_Pds extends Zend_Db_Table_Row_Abstract which doesn't have a current method. (Only Zend_Db_Table_Rowset_Abstract has it)
Tell the Application_Model_DbTable_Pds which $_rowClass to use:
class Application_Model_DbTable_Pds extends Zend_Db_Table_Abstract
{
...
protected $_rowClass = 'Application_Model_Pds';
}
In this way you don't have to pass it to the mapper:
public function find($id)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
// better return null or throw exception?
return;
}
return $result->current();
}
Also you don't need the properties for the db fields. They should be created automatically with the names from db.
class Application_Model_Pds extends Zend_Db_Table_Row_Abstract
{
public function setId($id)
{
$this->id = (int)$id;
}
public function getId()
{
return $this->id;
}
...
}
In the controller:
public function indexAction()
{
$pds = $o_mapper->find('69000001');
$specialiteByPds = $pds->findDependentRowset('Application_Model_DbTable_Specialite');
$this->view->pds = $pds;
$this->view->specialite = $specialiteByPds;
}
Note: Not tested! And I'm not sure if the relationships work. Have a look here for more info.
Personally I like it more to have an independent model class like described here. It seems you tried to mix this concept with the Zend_Db_Table_Row_Abstract as model concept.
Related
I have the following problem with doctrine when testing a symfony 5 application. Instead of updating the rows in the database, new rows are created when the persist () method is called or when cascade = {"persist"} is defined in Entity. The above issue only occurs in a test environment. Everything works fine in the app itself.
sample test code (maximally simplified to show the problem)
class GetReadyArticlesTest extends FunctionalDBTest
{
protected function setUp():void
{
parent::setUp();
$this->addFixture(new ConfigurationFixtures());
$this->addFixture(new CopyWriterTextOrderFixtures());
$this->executeFixtures();
}
protected function tearDown(): void
{
parent::tearDown();
}
public function testProcessSaveArticles()
{
$textOrderRepository = $this->entityManager->getRepository(CopywriterTextOrder::class);
$textOrderEntity = $textOrderRepository->find(1);
$textOrderEntity->setCharacters(4500);
$this->entityManager->persist($textOrderEntity);
$this->entityManager->flush();
}
}
Abstract class FunctionalDBTest:
abstract class FunctionalDBTest extends FunctionalTest
{
/**
* #var EntityManagerInterface
*/
protected $entityManager;
/**
* #var ORMExecutor
*/
private $fixtureExecutor;
/**
* #var ContainerAwareLoader
*/
private $fixtureLoader;
protected function setUp(): void
{
parent::setUp();
if ($this->getContainer()->getParameter('kernel.environment') !== 'test') {
die('Wymagane środowisko testowe');
}
$this->entityManager = $this
->getContainer()
->get('doctrine')
->getManager();
$schemaTool = new SchemaTool($this->entityManager);
$schemaTool->updateSchema($this->entityManager->getMetadataFactory()->getAllMetadata());
}
protected function addFixture(FixtureInterface $fixture): void
{
$this->getFixtureLoader()->addFixture($fixture);
}
protected function executeFixtures(): void
{
$this->getFixtureExecutor()->execute($this->getFixtureLoader()->getFixtures());
}
private function getFixtureExecutor(): ORMExecutor
{
if (!$this->fixtureExecutor) {
$this->fixtureExecutor = new ORMExecutor($this->entityManager, new ORMPurger($this->entityManager));
}
return $this->fixtureExecutor;
}
private function getFixtureLoader(): ContainerAwareLoader
{
if (!$this->fixtureLoader) {
$this->fixtureLoader = new ContainerAwareLoader($this->getContainer());
}
return $this->fixtureLoader;
}
protected function tearDown(): void
{
(new SchemaTool($this->entityManager))->dropDatabase();
parent::tearDown();
$this->entityManager->close();
$this->entityManager = null;
}
}
Removing the persist () method in this example fixes the problem. But in case I want to save a new relation to the table, it also generates a new main object. the problem only occurs in tests.
I have an entity Guest with :
#[ApiFilter(NumericFilter::class, properties: ['tenant'])]
class Guest implements UserInterface
{
#[ORM\Column(type: 'smallint')]
private $tenant;
...
When I call api/guests?tenant=963, I get my guests with a tenant in 963.
But I would like to change the value of tenant by a subscriber
My last suscriber (i tried a good 20 combinations)
use Symfony\Component\HttpFoundation\RequestStack;
class ApiPlatformSubscriber implements EventSubscriberInterface
{
private $security;
public function __construct(Security $security, RequestStack $requestStack, AuthController $AuthController)
{
$this->security = $security;
$this->AuthController = $AuthController;
$this->user = $this->security->getUser();
$this->request = $requestStack->getMainRequest();
}
/**
* filtre les resultats des GET par le numéro tenant
*
* #return void
*/
public function filterByTenant($event)
{
if($this->request->query->get('tenant')){
$this->request->query->set('tenant', 900);
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST=> 'filterByTenant', EventPriorities::PRE_READ
];
}
But it doesn't work at all, someone have an idea ? thanks
I am trying to use sessions in Symfony version 5.3.9 with RequestStack because SessionInterface is deprecated.
I get the following error:
Cannot use object of type Symfony\Component\HttpFoundation\Session\Session as array
here:
if(isset($cart[$id])){ (in my addToCart function)
in symfony 5.2 it was ok
Thank you for your help
My CartController.php :
<?php
namespace App\Controller;
use App\Services\CartServices;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CartController extends AbstractController
{
/**
* #Route("/cart", name="cart")
*/
public function index(CartServices $cartServices): Response
{
$cartServices->addToCart(3);
dd($cartServices->getCart());
return $this->render('cart/index.html.twig', [
'controller_name' => 'CartController',
]);
}
}
My CartServices.php :
<?php
namespace App\Services;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;
class CartServices
{
private $requestStack;
private $repoProduct;
public function __construct(RequestStack $requestStack, ProductRepository $repoProduct)
{
$this->requestStack = $requestStack;
$this->repoProduct = $repoProduct;
}
public function addToCart($id){
$cart = $this->getCart();
if(isset($cart[$id])){
$cart[$id]++;
}else{
$cart[$id] = 1;
}
$this->updateCart($cart);
}
$cart = $this->getCart():
public function getCart(){
return $this->requestStack->getSession('cart', []);
}
Thank you very much but I still have no results
My CartServices.php
<?php
namespace App\Services;
use App\Repository\ProductRepository;
use Symfony\Component\HttpFoundation\RequestStack;
class CartServices
{
private $requestStack;
private $repoProduct;
public function __construct(RequestStack $requestStack, ProductRepository $repoProduct)
{
$this->requestStack = $requestStack;
$this->repoProduct = $repoProduct;
}
public function addToCart($id){
$cart = $this->getCart();
if(isset($cart[$id])){
//produit déjà dans le panier on incrémente
$cart[$id]++;
}else{
//produit pas encore dans le panier on ajoute
$cart[$id] = 1;
}
$this->updateCart($cart);
}
public function deleteFromCart($id){
$cart = $this->getCart();
//si produit déjà dans le panier
if(isset($cart[$id])){
//si il y a plus d'une fois le produit dans le panier on décrémente
if($cart[$id] >1){
$cart[$id] --;
}else{
//Sinon on supprime
unset($cart[$id]);
}
//on met à jour la session
$this->updateCart($cart);
}
}
public function deleteAllToCart($id){
$cart = $this->getCart();
//si produit(s) déjà dans le panier
if(isset($cart[$id])){
//on supprime
unset($cart[$id]);
}
//on met à jour la session
$this->updateCart($cart);
}
public function deleteCart(){
//on supprime tous les produits (on vide le panier)
$this->updateCart([]);
}
public function updateCart($cart){
$this->requestStack->getSession('cart', $cart);
}
public function getCart(){
$session = $this->requestStack->getSession();
return $session->get('cart', []);
}
public function getFullCart(){
$cart = $this->getCart();
$fullCart = [];
foreach ($cart as $id => $quantity){
$product = $this->repoProduct->find($id);
if($product){
//produit récupéré avec succés
$fullCart[]=[
'quantity' => $quantity,
'product' => $product
];
}else{
//id incorrect
$this->deleteFromCart($id); //on ne met pas à jour la session car cette method le fait aussi (voir plus haut dans la fonction deleteFromCart)
}
}
}
}
My CartController.php
<?php
namespace App\Controller;
use App\Services\CartServices;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class CartController extends AbstractController
{
/**
* #Route("/cart/add/{id}")
*/
public function addToCart($id,CartServices $cartServices):Response
{
$cartServices->addToCart($id);
dd($cartServices->getCart(1));
return $this->render('cart/index.html.twig', [
'controller_name' => 'CartController',
]);
}
}
the method getSession of RequestStack return an object of SessionInterface, so your code is not correct, bellew the body of the method :
/**
* Gets the current session.
*
* #throws SessionNotFoundException
*/
public function getSession(): SessionInterface
{
if ((null !== $request = end($this->requests) ?: null) && $request->hasSession()) {
return $request->getSession();
}
throw new SessionNotFoundException();
}
So, you should update your method getCart like this :
public function getCart(){
$session = $this->requestStack->getSession();
return $session->get('cart', []);
}
public function getCart(){
return $this->requestStack->getSession('cart', []);
}
I have two simple entities : Cabinet and Personne with "OneToOne" relation. But when I add Cabinet, I have this error : Column 'personne_id' cannot be null.
Cabinet entity :
class Cabinet
{
/**
* #ORM\OneToOne(targetEntity="LogicielBundle\Entity\Personne", cascade={"persist","remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $personne;
public function setPersonne(\LogicielBundle\Entity\Personne $personne)
{
$this->personne = $personne;
return $this;
}
public function getPersonne()
{
return $this->personne;
}
Personne entity :
class Personne {
/**
* #ORM\OneToOne(targetEntity="UtilisateurBundle\Entity\Cabinet", cascade={"persist","remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $cabinet;
public function setCabinet(\UtilisateurBundle\Entity\Cabinet $cabinet)
{
$this->cabinet = $cabinet;
return $this;
}
public function getCabinet()
{
return $this->cabinet;
}
My Controller is very simple :
public function ajouterAction(Request $request)
{
$personne = new Personne();
$cabinet = new Cabinet();
$cabinet->setPersonne($personne);
$personne->setCabinet($cabinet);
$form = $this->createForm('utilisateur_cabinet_form', $cabinet);
$this->submit($form);
if ($form->handleRequest($request)->isValid()) {
$em = $this->getDoctrine()->getManager();
dump($personne);
dump($cabinet);
$em->persist($cabinet);
$em->flush();
$request->getSession()->getFlashBag()->add('success', 'Le cabinet « '.$cabinet->getVersions()[0]->getLibelle().' » a été ajouté.');
return $this->redirect($this->generateUrl('utilisateur_cabinet_index'));
}
return array(
'form' => $form->createView(),
'title' => 'Ajouter un nouveau cabinet'
);
}
dump($cabinet) ans dump($personne) is true ; can you help me ?
Make sure to allow the Setter parameter to be null as well (nullable=true isn't enough here):
public function setPersonne(\LogicielBundle\Entity\Personne $personne = null)
I have the following method in my repository and I want to test it
public function myFindOne($id)
{
// On passe par le QueryBuilder vide de l'EntityManager pour l'exemple
$qb = $this->_em->createQueryBuilder();
$qb->select('a')
->from('xxxBundle:entity', 'a')
->where('a.id = :id')
->setParameter('id', $id);
return $qb->getQuery()
->getResult();}
I found the following code in the documentation, but I could not understand it
// src/Acme/StoreBundle/Tests/Entity/ProductRepositoryFunctionalTest.php
namespace Acme\StoreBundle\Tests\Entity;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ProductRepositoryFunctionalTest extends WebTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* {#inheritDoc}
*/
public function setUp()
{
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager()
;
}
public function testSearchByCategoryName()
{
$products = $this->em
->getRepository('AcmeStoreBundle:Product')
->searchByCategoryName('foo')
;
$this->assertCount(1, $products);
}
/**
* {#inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
$this->em->close();
}
}
To see what you should edit in this code, the testSearchByCatergory() should be a good start. In this example, it gets the result of the tested method into $products and checks that this collection contains only one element.
So I guess your test would be to test that the returned object is the one you expect to be returned. But heh, like #cheesemacfly said, your repo is kinda the same as findOne[ById]()... Oh and BTW, you should check up phpunit [EN] (Or in FR, as I saw in your comment) documentation to see how you should make it run.
Cheers. :)
From Symfony's official documentation, the Repository methods should be tested as follow:
// tests/AppBundle/Entity/ProductRepositoryTest.php
namespace Tests\AppBundle\Entity;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class ProductRepositoryTest extends KernelTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* {#inheritDoc}
*/
protected function setUp()
{
self::bootKernel();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager();
}
public function testSearchByCategoryName()
{
$products = $this->em
->getRepository('AppBundle:Product')
->searchByCategoryName('foo')
;
$this->assertCount(1, $products);
}
/**
* {#inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
$this->em->close();
}
}