How can I access {{ app.session.get('user') }}
I don't understand much about controllers I only have this is the one I use to control that template
//accedemos a las dependencias de Twig
require_once './vendor/autoload.php';
use Symfony\Component\HttpFoundation\Session\Session;
//accedemos a la clase LoginModel para realizar operaciones a la base de datos
require_once './model/LoginModel.php';
require_once './core/ControladorBase.php';
class LoginController
{
private $loader;
private $twig;
private $controladorBase;
public function __construct(){
$this->controladorBase = new ControladorBase();
$this->loader = new \Twig\Loader\FilesystemLoader('./view');
$this->twig = new \Twig\Environment($this->loader);
}
/**
* Muestra la página principal y el listado de empleados dados de alta
* #throws \Twig\Error\LoaderError
* #throws \Twig\Error\RuntimeError
* #throws \Twig\Error\SyntaxError
*/
public function index(){
$session = new Session();
$session->set('user', 'juan' );
echo $this->twig->render('/login/index.html.twig');
}
}
Related
I have a problem with Oauth2. It returns me as error "Invalid state parameter passed in callback URL."
I'm on symfony 6
I don't know where this can come from, in localhost everything works but in production I have this error.
I looked everywhere for a solution without finding anything.
knpu_oauth2_client.yaml
knpu_oauth2_client:
clients:
azure:
type: azure
client_id: '%env(OAUTH_AZURE_CLIENT_ID)%'
client_secret: '%env(OAUTH_AZURE_CLIENT_SECRET)%'
redirect_route: connect_azure_check
redirect_params: {}
# scope: {}
tenant: '%env(AZURE_TENANT_ID)%'
security.yaml
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
users_in_memory: { memory: null }
my_provider:
entity: {class: App\Entity\User, property: uuid}
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: my_provider
custom_authenticators:
- App\Security\AzureAuthenticator
logout: true
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/connect/azure, role: PUBLIC_ACCESS }
- { path: ^/, roles: ROLE_USER}
AzureController.php
<?php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class AzureController extends AbstractController
{
/**
* Cette fonction effectue la connexion avec Azure
* Ex: Si vous allez sur cette route, un formulaire microsoft vous demandera de vous connecter
*/
#[Route('/connect/azure', name: 'connect_azure', )]
public function connectAction(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('azure')
->redirect([
'openid', 'profile', 'email'
], []);
}
/**
* Cette fonction permet de savoir si l'authentification à réussi
* Ex: Après vous être connecté ci-dessus, vous serez rediriger sur cette route qui vous redirigera à son tour vers la route home
*/
#[Route('/connect/azure/check', name: 'connect_azure_check', schemes:['http'])]
public function connectCheckAction(Request $request, ClientRegistry $clientRegistry)
{
try {
return $this->redirectToRoute('home');
} catch (IdentityProviderException $e) {
return new JsonResponse(array('status' => false, 'message' => "User not found!", 'error' => $e->getMessage()));
}
}
}
AzureAuthenticator.php
<?php
namespace App\Security;
use App\Entity\User;
use League\OAuth2\Client\Provider\azureUser;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class AzureAuthenticator extends OAuth2Authenticator implements AuthenticationEntryPointInterface
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $entityManager;
private RouterInterface $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->entityManager = $entityManager;
$this->router = $router;
}
/**
* Cette fonction renvoie true alors la fonction authenticate sera appelée
* #param Request $request
* #return bool|null
*/
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'connect_azure_check';
}
/**
* Cette fonction permet de traiter les données et de les utiliser. Elle vérifie également si l'utilisateur est déjà existant en base de donnée, si se n'est pas le cas elle l'ajoute.
* #param Request $request
* #return Passport
*/
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('azure');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) {
/** #var AzureUser $AzureUser */
$AzureUser = $client->fetchUserFromToken($accessToken);
// 1) have they logged in with Azure before? Easy!
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['uuid' => $AzureUser->getId()]);
if ($existingUser) {
return $existingUser;
}
$user = new User();
$user->setUuid($AzureUser->getId());
$user->setNom($AzureUser->claim('family_name'));
$user->setPrenom($AzureUser->claim('given_name'));
$user->setEmail($AzureUser->claim('upn'));
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
/**
* Si l'authentification réussi, l'utilisateur sera renvoyé sur la route home
* #param Request $request
* #param TokenInterface $token
* #param string $firewallName
* #return Response|null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$targetUrl = $this->router->generate('home');
return new RedirectResponse($targetUrl);
}
/**
* Si l'authentification échoue, l'utilisateur sera informé avec un message d'erreur
* #param Request $request
* #param AuthenticationException $exception
* #return Response|null
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
/**
* Cette fonction permet de rediriger l'utilisateur sur la route de connexion
* Dès que l'utilisateur sera sur une route qu'il n'a pas le droit d'avoir accès il sera rediriger à cet endroit (Dans le cas de notre application toutes les routes sont par défaut interdite)
* #param Request $request
* #param AuthenticationException|null $authException
* #return Response
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse(
'/connect/azure',
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
When I add use_state: true in parameter another error is returned: "Authentication failed! Did you authorize our app?"
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'm making a command in Laravel 7 that creates some migrations in a folder inside database/migrations and a seeder. Then, it runs dumpAutoloads() to make sure that the created migrations and seeder are registered in the autoload classmap. Then, my command calls the php artisan migrate command and consecutively the php artisan db:seed command with the --class flag to only seed the created seeder.
Everything runs fine until the db:seed command is called. The seeder is indeed created, but it keeps throwing me the next exception:
Illuminate\Contracts\Container\BindingResolutionException
Target class [StudentOperationTypesSeeder] does not exist
This is obviously just an example, but I have checked that the name of the created Seeder is exactly the same as the one the exception shows and it matches. Also, just after this exception is thrown to me, I run by myself the db:seed --class=StudentOperationTypesSeeder command and it works!
This makes me think that maybe the autoload classmap isn't updated until the process of the command is finished or something... I really have no idea.
My code is the following:
TimeMachineGeneratorCommand.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
class TimeMachineGeneratorCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'time-machine:generate {table_name}';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Generates the tables and structure of a time machine for the given table.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
// Convierte a snake case singular el nombre de la entidad.
$entity = Str::singular(
Str::snake(class_basename($this->argument('table_name')))
);
// Revisa que el nombre del modelo esté en el formato adecuado.
$modelName = Str::singular(
Str::studly($this->argument('table_name'))
);
$seederClassName = "{$modelName}OperationTypesSeeder";
// Crea las migraciones para la estructura de la máquina del tiempo.
$this->createMigrations($entity);
// Genera el Seeder del los operation types básicos.
$this->createSeeder($seederClassName);
// Para asegurarse que las migrations y el seeder está registrada en
// los class loaders se ejecuta el dump-autoload.
$this->line("<fg=yellow>Dumping Autoloads...</>");
$this->laravel->composer->dumpAutoloads();
$this->info("Autoloads dumped.");
// Ejecuta las migraciones.
$this->call('migrate', [
'--path' => '/database/migrations/'.Str::plural(Str::snake($modelName)).'/'
]);
// Ejecuta el seeder recién creado.
$this->call('db:seed', [
'--class' => $seederClassName
]);
(...) // <-- Some other code that isn't executed because of the exception
}
/**
* Genera los archivos de código de las migraciones necesarias
* para la estructura de la máquina del tiempo.
*/
private function createMigrations($entity)
{
// Genera la migración para los tipos de operación de la entidad.
$this->call('time-machine:operation-type-migration', [
'entity' => $entity
]);
// Genera la migración para los logs de la entidad.
$this->call('time-machine:log-migration', [
'entity' => $entity
]);
// Genera la migración para los logs de la entidad.
$this->call('time-machine:required-fields-migration', [
'entity' => $entity
]);
}
/**
* Crea el seeder de operationt types basado en el
* template diseñado para la máquina del tiempo.
*
* #param string | $seederClassName | El nombre de la clase para el seeder.
* #return void.
*/
private function createSeeder($seederClassName)
{
$this->call('time-machine:seeder', [
'name' => $seederClassName
]);
// Agrega el seeder recién creado al DatabaseSeeder.php.
$this->updateDatabaseSeeder($seederClassName);
}
/**
* Agrega el seeder recién creado al DatabaseSeeder.php.
*
* #var $seederClassName.
* #return void.
*/
private function updateDatabaseSeeder($seederClassName)
{
$filePath = base_path().'\\database\\seeds\\DatabaseSeeder.php';
// Lee el archivo del DataBaseSeeder.
$seeder = file_get_contents($filePath);
// Si el seeder no ha sido agregado ya al DatabaseSeeder.php...
if(preg_match('/\$this\-\>call\('.$seederClassName.'\:\:class\)\;/', $seeder) == 0) {
// Agrega el seeder recién creado.
$newContent = preg_replace(
'/public function run\(\)\s*\{/',
"public function run()
{
\$this->call({$seederClassName}::class);",
$seeder, 1
);
// Guarda el contenido del archivo.
file_put_contents($filePath, $newContent);
$this->info('Seeder added to DatabaseSeeder.php.');
} else {
$this->error('Seeder is already in DataBaseSeeder.php.');
}
}
}
Also, this is the autoload section of my composer.json (I read there may be something with that or something, I couldn't find a solution for my issue anyway).
"autoload": {
"psr-4": {
"App\\": "app/"
},
"classmap": [
"database/seeds",
"database/factories"
]
},
StudentOperationTypesSeeder.php
<?php
use Illuminate\Database\Seeder;
class StudentOperationTypesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$studentOperationType = new \App\StudentOperationType();
$studentOperationType->description = "Created";
$studentOperationType->save();
$studentOperationType = new \App\StudentOperationType();
$studentOperationType->description = "Updated";
$studentOperationType->save();
$studentOperationType = new \App\StudentOperationType();
$studentOperationType->description = "Deleted";
$studentOperationType->save();
}
}
Please help. The command generates succesfully the migrations and the seeder, then runs the migrations and everything works exactly as intended, except for the seeder and I haven't find why.
Note: The other "time-machine" commands that I call in the functions of the TimeMachineGeneratorCommand.php are other custom commands that I created that literally only extend the existing migration commands of the vendor and change the stub to a custom one.
I followed the approach that lagbox suggested and found something that works.
I included the generated file adding the next line of code before calling the bd:seed command, obviously after the line that generates the seeder.
include base_path()."\\database\\seeds\\".$seederClassName.".php";
After doing this the seeder is executed correctly and the whole command works just as expected.
The final handle method looks as follows.
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
// Convierte a snake case singular el nombre de la entidad.
$entity = Str::singular(
Str::snake(class_basename($this->argument('table_name')))
);
// Revisa que el nombre del modelo esté en el formato adecuado.
$modelName = Str::singular(
Str::studly($this->argument('table_name'))
);
$seederClassName = "{$modelName}OperationTypesSeeder";
// Crea las migraciones para la estructura de la máquina del tiempo.
$this->createMigrations($entity);
// Genera el Seeder del los operation types básicos.
$this->createSeeder($seederClassName);
// Para asegurarse que las migrations y el seeder está registrada en
// los class loaders se ejecuta el dump-autoload.
$this->line("<fg=yellow>Dumping Autoloads...</>");
$this->laravel->composer->dumpAutoloads();
$this->info("Autoloads dumped.");
// Ejecuta las migraciones.
$this->call('migrate', [
'--path' => '/database/migrations/'.Str::plural(Str::snake($modelName)).'/'
]);
include base_path()."\\database\\seeds\\".$seederClassName.".php";
// Ejecuta el seeder recién creado.
$this->call('db:seed', [
'--class' => $seederClassName
]);
(...) // <-- Some other code that isn't executed because of the exception
}
I'm running two test XAMPP servers, both with similar PDO Connection classes
First:
<?php
/**
* Clase que envuelve una instancia de la clase PDO
* para el manejo de la base de controladores
*/
require_once 'login_mysql.php';
class ConexionBD
{
/**
* Única instancia de la clase
*/
private static $db = null;
/**
* Instancia de PDO
*/
private static $pdo;
final private function __construct()
{
try {
// Crear nueva conexión PDO
self::obtenerBD();
} catch (PDOException $e) {
// Manejo de excepciones
}
}
/**
* Retorna en la única instancia de la clase
* #return ConexionBD|null
*/
public static function obtenerInstancia()
{
if (self::$db === null) {
self::$db = new self();
}
return self::$db;
}
/**
* Crear una nueva conexión PDO basada
* en las constantes de conexión
* #return PDO Objeto PDO
*/
public function obtenerBD()
{
if (self::$pdo == null) {
self::$pdo = new PDO(
'mysql:dbname=' . BASE_DE_DATOS .
';host=' . NOMBRE_HOST . ";",
USUARIO,
CONTRASENA,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
);
// Habilitar excepciones
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$pdo;
}
/**
* Evita la clonación del objeto
*/
final protected function __clone()
{
}
function _destructor()
{
self::$pdo = null;
}
}
Second (this one doesn't work):
<?php
/**
* Clase que envuelve una instancia de la clase PDO
* para el manejo de la base de datos
*/
require_once 'mysql_login.php';
class Database
{
/**
* Única instancia de la clase
*/
private static $db = null;
/**
* Instancia de PDO
*/
private static $pdo;
final private function __construct()
{
try {
// Crear nueva conexión PDO
self::getDb();
} catch (PDOException $e) {
// Manejo de excepciones
}
}
/**
* Retorna en la única instancia de la clase
* #return Database|null
*/
public static function getInstance()
{
if (self::$db === null) {
self::$db = new self();
}
return self::$db;
}
/**
* Crear una nueva conexión PDO basada
* en los datos de conexión
* #return PDO Objeto PDO
*/
public function getDb()
{
if (self::$pdo == null) {
self::$pdo = new PDO(
'mysql:dbname=' . DATABASE .
';host=' . HOSTNAME .
';port:63343;',
USERNAME,
PASSWORD,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")
);
// Habilitar excepciones
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$pdo;
}
/**
* Evita la clonación del objeto
*/
final protected function __clone()
{
}
function _destructor()
{
self::$pdo = null;
}
}
?>
And for some reason the second connection is not resolved. Any ideas why this might be?
Both files are on different directories and are referenced by different, isolated files.
EDIT: Error returned
exception 'PDOException' with message 'SQLSTATE[HY000] [2002] Connection refused' in /opt/lampp/htdocs/estudiantesServer/Database.php:60 Stack trace:
#0 /opt/lampp/htdocs/estudiantesServer/Database.php(60): PDO->__construct('mysql:host=loca...', 'root', 'root', Array)
#1 /opt/lampp/htdocs/estudiantesServer/Database.php(27): Database->getDb()
#2 /opt/lampp/htdocs/estudiantesServer/Database.php(42): Database->__construct()
#3 /opt/lampp/htdocs/estudiantesServer/estudiantes.php(26): Database::getInstance()
#4 /opt/lampp/htdocs/estudiantesServer/getEstudiantes.php(10): Estudiantes::getAll()
#5 {main}
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.