Doctrine 2 - Get Entity manager from class - php

I can't figure out how it is best to get the Doctrine Entity Manager from my service layers, and template controller..
I thinking of making a singleton so i always can get the Entity manager, but is it the right way to do it?
Updated: I'll take an example
class Auth
{
const USER_ENTITY_NAME = 'Entities\User';
private $isVerified = FALSE;
public static function login($email, $password, $em, $rememberMe = false)
{
if(empty($email) OR empty($password))
{
// new login response
}
if($user = (self::getUser($email, $password, $em) !== null))
{
$sreg = SessionRegistry::instance();
$sreg->set("user_id", $user->getId());
}
return $user;
}
public static function getUser($email, $password, $em)
{
return $em->getRepository(
USER_ENTITY_NAME );
}
What i cant figure out is where i should get the user from? so i doesn't have to send the entity manager as an parameter.

Choose dependency injection over singleton.
I don't know which environment are you using Doctrine in, but I assume it being MVC - then any Controller should have access to the entity manager, either by passing it as a constructor argument, either by injecting it with a setter.
This way you can fetch stuff from the controller, and pass it to the Auth class eventually.
Anyway I think that authorization doesn't need an external class - I'd just write a loginAction method in a controller, get username and password from HTTP request and make the usual considerations [fetch the user / check if password is right], then store something in session in case of succesful login.

Related

How we can handle 50 Dependency injection in single controller in Laravel?

How we can inject 50 or 100 Injections in laravel ,because if we inject that much,it will not be a good way. It Slows the process.
//Just for example : -
public function __construct(
RoleManagement $role_management,
UserRole $userRole,
User $user
RoleManagement $role_management,
UserRole $userRole,
User $user ,
RoleManagement $role_management,
UserRole $userRole,
User $user ,
RoleManagement $role_management,
UserRole $userRole,
User $user,
RoleManagement $role_management,UserRole $userRole,User $user)
{
$this->role_management = $role_management;
$this->userRole = $userRole;
$this->user = $user;
$this->role_management = $role_management;
$this->userRole = $userRole;
$this->user = $user;
$this->role_management = $role_management;
$this->userRole = $userRole;
$this->user = $user;
}
Actually, it wouldn't really slow the process as far as I know (the container is compiled, so it comes down to passing parameters during a function call and a few assignation, which is not specifically slower).
Your problem here is a bad design as stated by aynber, your class/method should not depend on that many collaborators.
You can refer more specifically to the S of SOLID (Single Responsibility Principle) and to the 8th object calisthenics.
We do not have details on the actual content of the method, but it all comes down to splitting the process in different classes that can be injected one in another.

How does DBAL read data that ORM inserts but has not yet “flush”?

For historical reasons, my pattern of running databases using Symfony is mixed. That is, the query uses DBAL and the insert uses ORM. Now you need to write a lot of data to the database. The flush in ORM can help me achieve business at the lowest cost.
All flush operations have been removed from the project. Put it in the __destruct of the controller.
However, doing so will cause DBAL to not find the latest changed data. Of course, these data ORMs can be obtained normally.
This is a very difficult problem. I hope to get guidance.
class BaseController extends Controller
{
public function __destruct()
{
$this->getDoctrine()->getManager()->flush();
}
public function indexAction()
{
$model = new CompanyModel();
$model->install(['company_name' => '1234']);
$model->update(['company_name' => 'abcd'], $model->lastInsertId);
}
}
class CompanyModel extends BaseController
{
public function validate($data, $id = false)
{
$this->entityManager = $this->getDoctrine()->getManager();
if(empty($id)){
$this->company_class = new Company();
}else{
if(!$this->is_exist($id)){
return false;
}
$this->company_class = $this->entityManager->getRepository(Company::class)->find($id);
}
if(array_key_exists('company_name', $data)){
$this->company_class->setCompanyName($data['company_name']);
}
if(self::$error->validate($this->company_class)){
return false;
}
return true;
}
public function insert($data)
{
if(!$this->validate($data)){
return false;
}
$this->company_class->setCreateAt(new \DateTime());
$this->entityManager->persist($this->company_class);
//$this->entityManager->flush();
$this->lastInsertId = $this->company_class->getId();
return true;
}
public function update($data, $id)
{
if(empty($id)){
self::$error->setError('param id is not null');
return false;
}
if(!$this->validate($data, $id)){
return false;
}
$this->company_class->setUpdateAt(new \DateTime());
//$this->entityManager->flush();
return true;
}
public function is_exist($id)
{
return $this->get('database_connection')->fetchColumn('...');
}
}
The final result of executing indexAction company_name is 1234; $ model-> update() was not executed successfully. The reason is that the $this-> is_exist() method that took the DBAL query did not find the ORM insert but did not flush the message.
Unchanging conditions,run
$this->entityManager->getRepository(Company::class)->find($id);
Is successful。
The problem is not the entity manager or dbal, as far as I can tell, but the usage of an anti-pattern, which I would call ... entanglement. What you should strive for is separation of concerns. Essentially: Your "CompanyModel" is an insufficient and bad wrapper for the EntityManager and/or EntityRepository.
No object should know about the entity manager. It should only be concerned with holding the data.
The entity manager should be concerned with persistence and ensuring integrity.
The controller is meant to orchestrate one "action", that can be adding one company, editing one company, batch-importing/updatig many companies.
Services can be implemented, when actions become to business-logic-heavy or when functionality is repeated.
(Note: the following code samples could be made way more elegant with using all the features that symfony provide, like ParamConverters, the Form component, the Validation component, I usually wouldn't write code this way, but I assume everything else would go way over your head - no offence.)
handling actions in the controller
controller actions (or service actions, really) are when you look at your problem from the task perspective. Like "I want to update that object with this data"). That's when you fetch/create that object, then give it the data.
use Doctrine\ORM\EntityManagerInterface;
class BaseController extends Controller {
public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
public function addAction() {
$company = new Company(['name' => '1234']); // initial setting in constructor
$this->em->persist($company);
// since you have the object, you can do any changes to it.
// just change the object
$company->update(['name' => 'abcd']); // <-- don't need id
// updates will be flushed as well!
$this->em->flush();
}
public function editAction($id, $newData) {
$company = $this->em->find(Company::class, $id);
if(!$company) {
throw $this->createNotFoundException();
}
$company->update($newData);
$this->em->flush();
}
// $companiesData should be an array of arrays, each containing
// a company with an id for update, or without an id for creation
public function batchAction(array $companiesData) {
foreach($companies as $companyData) {
if($companyData['id']) {
// has id -> update existing company
$company = $this->em->find(Company::class, $companyData['id']);
//// optional:
// if(!$company) { // id was given, but company does not exist
// continue; // skip
// // OR
// $company = new Company($companyData); // create
// // OR
// throw new \Exception('company not found: '.$companyData['id']);
// }
$company->update($companyData);
} else {
// no id -> create new company
$company = new Company($companyData);
$this->em->persist($company);
}
}
$this->em->flush(); // one flush.
}
}
the base controller should handle creating objects, and persisting it, so very basic business logic. some would argue, that some of those operations should be done in an adapted Repository for that class, or should be encapsulated in a Service. And they would be right, generally.
the entity handles it's internal state
Now, the Company class handles its own properties and tries to stay consistent. You just have to make some assumptions here. First of all: the object itself shouldn't care if it exists in the database or not. it's not its purpose! it should handle itself. Separation of concerns! The functions inside the Company entity should concern simple business logic, that concerns its INNER state. It doesn't need the database, and it should not have any reference to the database, it only cares about it's fields.
class Company {
/**
* all the database fields as public $fieldname;
*/
// ...
/**
* constructor for the inital state. You should never want
* an inconsistent state!
*/
public function __construct(array $data=[]) {
$this->validate($data); // set values
if(empty($this->createAt)) {
$this->createAt = new \DateTime();
}
}
/**
* update the data
*/
public function update(array $data) {
$this->validate($data); // set new values
$this->updateAt = new \DateTime();
}
public function validate(array $data) {
// this is simplified, but you can also validate
// here and throw exceptions and stuff
foreach($array as $key => $value) {
$this->$key = $value;
}
}
}
some notes
Now, there should be NO use case, where you get an object to persist and at the same time an update - with an id - that refers to the new object ... unless that object was given the id beforehand! HOWEVER. If you persist an object, that has an ID and you call $this->em->find(Company::class, $id) you would get that object back.
if you have many relations, there are always good ways to solve this problem without destroying separation of concerns! you should never inject an entity manager into an entity. the entity should not manage its own persistence! nor should it manage the persistence of linked objects. handling persistence is the purpose of the entity manager or entity repository. you should never need a wrapper around an object just to handle that object. be careful not to mix responsibilities of services, entities (objects) and controllers. In my example code, I have merged services and controllers, because in simple cases, it's good enough.

Storing a model instance as a service during the session

I want to access to profile of the current user across the application (read/write). The user profile is an in instance of User model. Is it possible to store it on session as a service? If not, what is best practice?
Here is my login code. (ajax-based login)
function loginAction()
{
$this->view->disable();
{
$request = (array)$this->request->getJsonRawBody();
$user = User::findFirstByUsername($request['username']);
if (password_verify($request['password'], $user->password)) {
$userModel = User::findFirst('username="'.$request['username'].'"');
$this->getDI()['session']->set('auth', $user->id);
$this->user = $user;
jsonResponse($user);
} else {
http_response_code(401);
jsonResponse(['message' => 'invalid']);
}
}
}
There is several ways to achieve that. Let me share with you the one I've used in my own project...
First I've created a Component to deal with authentication related stuff such as checking the current session status (guest, user, admin), storing the current user specs, authentication procedures, etc.
namespace MyApp\Components;
use Phalcon\Mvc\User\Component as PhComponent;
class Authentication extends PhComponent
{
// ...
}
Then, I've registered this component in the main App's DI container:
$di->setShared('authentication', 'MyApp\Components\Authentication');
So I can use from my controllers, views, etc. Like:
function homeAction()
{
//...
if($this->authentication->isGuest()) {
//...
}
Finally to store data using the session. Phalcon provide a persistent session bag that you can use to easily store a serialized version of the model in the current session:
class Authentication extends PhComponent
{
// ...
function authenticate($username, $password)
{
// ... Authentication logic
if($validCredentials) {
$this->persistent->currentUser = $userModel;
}
// ...
}
}

Laravel 4 custom Auth

First of all sorry about my english, I'll try to do my best.
Im new to Laravel, im trying to implement custom auth throught a SOAP WS, I declare new class that implement UserProviderInterface. I success on implement retrieveByCredentials and validateCredentials methods but since i dont have access to database or global users information i cant implement retrieveByID method. Is there any way to make custom Auth not based on users id's ?
I need:
- Login and validate user throught SOAP WS
- Store User Info returned by WS.
- Remember me functionality
- Secure routes based on logged user and level of access
- Logout
Implemented class:
<?php
namespace Spt\Common\Providers;
use Illuminate\Auth\UserProviderInterface;
use Illuminate\Auth\GenericUser;
use Illuminate\Auth\UserInterface;
class AuthUserProvider implements UserProviderInterface{
private $user;
public function __construct(){
$this->user = null;
}
public function retrieveByID($identifier){
return $this->user;
}
public function retrieveByCredentials(array $credentials){
$client = new \SoapClient('webserviceurl');
$res = $client->Validar_Cliente($credentials);
$res = $res->Validar_ClienteResult;
if($res->infoError->bError === true){
return;
}
$res->id = $res->id_cliente;
$user = new GenericUser((array) $res);
return $user;
}
public function validateCredentials(UserInterface $user, array $credentials){
//Assumed that if WS returned a User is validated
return true;
}
}
I think that re-implement UserProviderInterface its not the solution but i googled and not found other way
Any Idea?
You're almost done, apart from the fact that private variable $user of AuthUserProvider doesn't survive the current http request. If you cannot "retrieve by id" from your web service, I guess the only way is to store the entire user in the session - Laravel itself stores the user's id in the session and the fact that it stores only the id (not the entire user) is one of the reasons why a retrieveByID method is needed.
The following is only to clarify and is untested.
class AuthUserProvider implements UserProviderInterface {
public function retrieveByCredentials(array $credentials) {
$client = new \SoapClient('webserviceurl');
$res = $client->Validar_Cliente($credentials);
$res = $res->Validar_ClienteResult;
if($res->infoError->bError === true) {
return;
}
$res->id = $res->id_cliente;
Session::put('entireuser', $res);
$user = new GenericUser((array) $res);
return $user;
}
public function retrieveByID($identifier) {
$res = Session::get('entireuser');
return new GenericUser((array) $res);
}
// ...
}
If you can't retrieve by id from your web service, I guess you cannot either retrieve by remember token, so it may be impossible for you to implement the "remember me" functionality, unless you store part of users data in a second database (which at that point could be used in place of the session above).

how to Use zend_auth as a plugin

I'm working on my first user login in Zend Framework, but I'm a little confused with Zend_Auth. All the articles I read about it use it directly in the controller. But to me, it makes more sense, to work as a plugin
What do you guys think?
You can use it as a plugin, the only downside is that if you initialize the plugin in your bootstrap, then the plugin will be executed for every controller and action, since it would have to run before your controller.
You could extend Zend_Auth and add extra methods to set up the auth adapter and manage the storage, and then you can just call Your_Custom_Auth::getInstance() to get the auth instance and then you can check for auth in the preDispatcth() part of your controllers that need auth.
This way you can easily work with zend_auth in multiple places with less code
<?php
class My_User_Authenticator extends Zend_Auth
{
protected function __construct()
{}
protected function __clone()
{}
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
// example using zend_db_adapter_dbtable and mysql
public static function getAdapter($username, $password)
{
$db = Zend_Controller_Front::getInstance()
->getParam('bootstrap')
->getResource('db');
$authAdapter = new Zend_Auth_Adapter_DbTable($db,
'accounts',
'username',
'password');
$authAdapter->setIdentity($username)
->setCredential($password)
->setCredentialTreatment(
'SHA1(?)'
);
return $authAdapter;
}
public static function updateStorage($storageObject)
{
self::$_instance->getStorage()->write($storageObject);
}
}
// in your controllers that should be fully protected, or specific actions
// you could put this in your controller's preDispatch() method
if (My_User_Authenticator::getInstance()->hasIdentity() == false) {
// forward to login action
}
// to log someone in
$auth = My_User_Authenticator::getInstance();
$result = $auth->authenticate(
My_User_Authenticator::getAdapter(
$form->getValue('username'),
$form->getValue('password'))
);
if ($result->isValid()) {
$storage = new My_Session_Object();
$storage->username = $form->getValue('username');
// this object should hold the info about the logged in user, e.g. account details
My_User_Authenticator::getInstance()->updateStorage($storage); // session now has identity of $storage
// forward to page
} else {
// invalid user or pass
}
Hope that helps.
"Plugin" in ZF doesn't only mean "front controller plugin", also Action helpers, view helpers...
ZF guru Matthew Weier O'Phinney wrote an excellent article about creating action helpers, and guess what ?..
He illustrates it with an Auth widget !
http://weierophinney.net/matthew/archives/246-Using-Action-Helpers-To-Implement-Re-Usable-Widgets.html
don't forget to read the articles comments, as a lot of interesting Q&A are handled there

Categories