Codeigniter - Custom result object method accessing to non-public properties...without setter? - codeigniter

I just working on some project for my client, and probably I found some bug or missleading description in documentation.
I want to make a model, what is rerning results by custom classes, so I'm using custom_row_object method.
class UserModel extends CI_Model {
public function __construct(){
parent::__construct();
$this->load->library('Class_loader'); /custom spl loader
}
public function get_user_by_email($email, $password = null){
$this->db->where('email',$email);
$result = $this->db->get('User');
return $result->custom_row_object(0,'User');
}
}
and here is my class
class User {
private $idUser;
private $password;
private $email;
private $role;
public function __construct() {
echo 'instancja';
echo $this->password;
}
public function __set($name, $value) {
if ($name === 'password') {
$this->password = '****' // only for testing purposes :)
}
}
}
based on information here I understand that CI shouldn't have access to this properties directly, and should call a __set method.
But I'm so confused, because there is no any custom constructor, and my __set method should modify only a password property - but it won't, CI returns object with already setted properties directly from database! The only thing what I can do to modify them is adding a constructor and made changes in it like this
public function __construct(){
$this->password = '****';
}
So the question is - how it's possible? Is CI uses some reflection mechanism, or am I too stupid and I understood docs wrong ? :/

You can find the answer in the underlying Driver.
You probably use mysql as database and pdo / mysqli as a driver.
In both cases - Codeigniter uses the fetchObject (PDO) or fetch_object (MySQLi) methods.
You can find this lines of code for PDO here and for MySQLi here on the official Codeigniter Github Repository.
If you take a look on the documentation now - it cleary states
PDO:
When an object is fetched, its properties are assigned from respective column values, and afterwards its constructor is invoked.
MySQLi:
The mysqli_fetch_object() will return the current row result set as an object where the attributes of the object represent the names of the fields found within the result set.
Note that mysqli_fetch_object() sets the properties of the object before calling the object constructor.
Source - PHP Documentation:
PDOStatement::fetchObject
mysqli_result::fetch_object

Related

Laravel Backpack - getting current record from crud controller

In my crud controller I am trying to get the name of the person who is currently being edited.
so
http://192.168.10.10/admin/people/93/edit
In the people crud controller
public function setup() {
dd(\App\Models\People::get()->first()->name)
}
This returns the first person not the person currently being edited.
How do I return the current person (with an id of 93 in this example)
Ok, So since you use backpack look into CrudController to see how the method looks:
public function edit($id)
{
$this->crud->hasAccessOrFail('update');
$this->data['entry'] = $this->crud->getEntry($id);
$this->data['crud'] = $this->crud;
$this->data['fields'] = $this->crud->getUpdateFields($id);
$this->data['id'] = $id;
return view('crud::edit', $this->data);
}
So now you can overwrite the edit function and change whatever you want. You can even create a custom edit page if you so wish.
Setup on the other hand is usually used to add things like
$this->crud->addClause(...);
Or you can even get the entire constructor and put it in the setup method because setup call looks like this:
public function __construct()
{
// call the setup function inside this closure to also have the request there
// this way, developers can use things stored in session (auth variables, etc)
$this->middleware(function ($request, $next) {
$this->setup();
return $next($request);
});
}
So you could do something like \Auth::user()->id;
Also it's normal to work like this. If you only use pure laravel you will only have access to the current id in the routes that you set accordingly.
Rahman said about find($id) method. If you want to abort 404 exception just use method findOrFail($id). In my opinion it's better way, because find($id)->name can throw
"Trying to get property of non-object error ..."
findOrFail($id) first fetch user with specified ID. If doesn't exists just throw 404, not 500.
The best answer is:
public function edit($id)
{
return \App\Models\People::findOrFail($id);
}
Good luck.
you need person against id, try below
public function setup($id) {
dd(\App\Models\People::find($id)->name);
}

Laravel policies - passing the class as a variable to $user->can() method doesn't work

I have a route with dynamic model recognition. In other words, I take the desired model as an argument and use it in the controller. I have complex authorization in my app and I need to pass the model class name as a variable to the $user->can() method for using policies, but for some reason it doesn't work. Here's my code:
Policy:
public function view($user, Model $model) {
return $user->model_id == $model_id;
}
public function create($user) {
return $user->isAdmin();
}
Controller:
public function createModel($model) {
$model_class = $model . '::class';
if (Auth::user()->can('create', $model_class)) {
return $model_class::create();
}
return 'invalid_permissions';
}
If I hardcode the model class name it works. For example, if my model is 'Car' and in the controller I put:
if (Auth::user()->can('create', Car::class)) {
Anybody got any ideas why this is so and how to fix it? I hope that it's possible because I would have to change my whole concept if it isn't.
*Note: this is example code, not my actuall classes

ZF2 and EntityManager (Doctrine)

I have a problem. I try to get the Entity-Manager without a Controller, but I found no way.
At this time, I get the Entity-Manager like this:
(Controller)
public function getEntityManager()
{
if (null === $this->_em) {
$this->_em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->_em;
}
(Plugin)
public function getEntityManager()
{
if($this->_em == null){
$this->_em = $this->getController()->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->_em;
}
You see, I need allways a controller. But, if I need the EntityManager in a model, i have a problem. I can give the model the controller, but I think this is really a bad way.
Have you any idea to get the EntityManager without a controller?
The way I handle Doctrine is through Services, i do it like the following:
//some Controller
public function someAction()
{
$service = $this->getServiceLocator()->get('my_entity_service');
return new ViewModel(array(
'entities' => $service->findAll()
));
}
The Service->findAll() would look something like this:
public function findAll()
{
return $this->getEntityRepository()->findAll();
}
Now we need to define the my_entity_service. I do this inside my Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'my_entity_service' => 'Namespace\Factory\MyServiceFactory'
)
);
}
Next I create the Factory (note: this could also be done via anonymous function inside the Module.php)
<?php
namespace Namespace\Factory;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
use Namespace\Model\MyModel;
class MyServiceFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$myModel= new MyModel();
$myModel->setEntityManager($serviceLocator->get('Doctrine\ORM\EntityManager'));
return $myModel;
}
}
Now this is a lot to chew :D I totally get that. What is happening here is actually very simple though. Instead of creating your model and somehow get to the EntityManager, you call ZF2's ServiceManager to create the Model for you and inject the EntityManager into it.
If this is still confusing to you, I'll gladly try to explain myself better. For further clarification however I'd like to know about your use case. I.e.: what for do you need the EntityManager or where exactly do u need it.
This code example is outside of the question scope
Just to give you a live example of something I do via ServiceFactories with forms:
public function createService(ServiceLocatorInterface $serviceLocator)
{
$form = new ReferenzwertForm();
$form->setHydrator(new DoctrineEntity($serviceLocator->get('Doctrine\ORM\EntityManager')))
->setObject(new Referenzwert())
->setInputFilter(new ReferenzwertFilter())
->setAttribute('method', 'post');
return $form;
}
Your real question is "How to get an Instance of ServiceManager in my own classes"
For this, take a look at the docu: (bottom of page http://zf2.readthedocs.org/en/latest/modules/zend.service-manager.quick-start.html)
By default, the Zend Framework MVC registers an initializer that will
inject the ServiceManager instance, which is an implementation of
Zend\ServiceManager\ServiceLocatorInterface, into any class
implementing Zend\ServiceManager\ServiceLocatorAwareInterface. A
simple implementation looks like the following.
so implent the ServiceLocatorInterface in your classes and then inside your class you can call:
$this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
or any other service you have registered.

Zend Framework: Incomplete object error when passing a value from controller to view

I'm passing a user object from the controller to the view, then calling a method on that controller. I've done a print_r on the object in the view, so I know it's the right object with the right values. The current_user variable is an instance of the user class.
Here is the line in the layout that gives the error.
<?php echo $this->current_user->get_avatar_url(); ?>
Here is the method in the user class it's calling
public function get_avatar_url()
{
return !empty($this->avatar) ? $this->avatar : $this->fb_userid != '' ? "http://graph.facebook.com/".$this->fb_userid."/picture" : "/public/images/pukie.jpg";
}
This is the error I get
Fatal error: main() The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "User" of the object you are trying to operate on was loaded before unserialize() gets called or provide a __autoload() function to load the class definition in /home/breathel/public_html/application/views/layouts/layout.phtml on line 48
I'm including the full controller base where this in called in case it makes a difference
<?php
Zend_Loader::loadClass('Zend_Controller_Action');
Zend_Loader::loadClass('User');
class BaseController extends Zend_Controller_Action
{
protected $auth;
protected $current_user;
protected $db;
protected function initialize_values()
{
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity())
{
$this->current_user = $auth->getIdentity();
$this->view->current_user = $this->current_user;
}
$this->db = Zend_Registry::get('dbAdapter');
$this->view->controller_name = $this->_request->getControllerName();
$this->view->view_name = $this->_request->getActionName();
}
}
Zend Framework's authorisation module uses sessions to preserve identity across page load and is probably serialising the User model under the covers (especially if you're just assigning the result of a Zend_Auth_Adapter call).
Try including the User class before the first call to getIdentity() and see if that fixes it (even if you're confident you're not serialising it yourself).

CodeIgniter and Doctrine models

I´m having problems understanding some basic strategies using doctrine 2 in codeIgniter 2 development.
Background: CI is up and running with doctrine, I can get entities from database and save them.
Example:
I have few controllers, where I would like to list latest articles. In my pure CI application I would have 'getLatest' -method in my model. I then would call this in all of my controllers, loading correct view etc.
But now I have doctrine models and not sure how to do this. I just cant add same method to my model. What I have done is moved that getLatest-logic to controller and this does not look right. Now I would need to call other controllers from my actions to get these latest articles. Or should I really duplicate that code in every controller where I need it?
I am still struggling with this. My CI models and doctrine entities have same names and are located in same "application/models/" folder. These together cause several problems. I am trying to change this path, but cant get it work. I have used this library class for loading doctrine: http://wildlyinaccurate.com/integrating-doctrine-2-with-codeigniter-2/ Any tips?
Your getLatest functionality belongs in the model, not the controller. Each of your Doctrine 2 models has its own Repository, which you can get with $em->getRepository('Your\Model'). The default repository (\Doctrine\ORM\EntityRepository) has several methods that might be useful to you:
$repository = $em->getRepository('Your\Model');
$all_entities = $repository->findAll();
$some_entities = $repository->findBy(array(
'some_column' => 'some_condition'
), $order_by, $limit, $offset);
If Doctrine's built-in repository doesn't do what you need, you can create your own repository for any of your models:
class YourRepository extends EntityRepository
{
public function getLatest()
{
// Your getLatest logic here
return $this->_em->createQuery('SELECT m FROM Your\Model LIMIT 10');
}
}
Instead of having a "Model", you can wrap Doctrine components in a Service and keep your controller as simple as possible. Using MVC framework or not, it is important to follow SOLID, especially the Single Responsibility Principle (SRP).
Consider the following simplified example:
class UserService extends AbstractService
{
private $em;
private $repository;
public function __construct(EntityManager $em, UserRepository $repository)
{
$this->em = $em;
$this->repository = $repository;
}
public function create($data)
{
// business logic here
$user = new User();
$user->setUsername($data['username']);
$user->setEmail($data['email']);
$em->persist($this->user);
$em->flush();
}
public function getLatest()
{
// additional business logic could be implemented
return $this->repository->getLatest();
}
}
Then I can call the service from the Controller or from any other service:
class UserController extends Controller
{
/**
* #var UserService $service
*/
private $service;
public function __construct(UserService $service)
{
$this->service = $service;
}
public function create()
{
// some form validation here
$data = $this->input->post('user');
$result = $this->service->create($data);
// do something with the result
}
}
No, you should not neither duplicate nor move that code to controllers. Haven't tried that personally, but I think that doctrine Entities and CI models are completely independent. You may introduce some CI's models that incorporate that getLatest method, but really operate on Doctrine entities.
So you'll just use Doctrine entities and CI's models as usual. But don't forget to load doctrine library (if you bootstrapped as told in Doctrine manual) into models that use Doctrine entities.

Resources