codeigniter callback from private methods not working - php

I doing form validation in CodeIgniter using Form Validation Library and my custom callbacks.
public function insert_user()
{
if($this->input->post('submit')) {
// load form validation library
$this->load->library('form_validation');
// configurations
$config = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => 'required|callback_username_check'
)
);
$this->form_validation->set_rules($config);
// .... continue ....
}
}
When method is public, it is working as expected.
public function username_check($username)
{
// do some stuffs here
}
When I make method as private, it is not working.
private function username_check($username)
{
// do some stuffs here
}
Why callbacks from private methods are not working?
Why I need this?
Public methods in CodeIgniter controllers are accessible by URLs like an example above
http://example.com/controller_name/username_check/blabla
I don't want callback methods accessible publicly.

The callback function must be public. Codeigniter Form validation class access your function at current controller so it may not be private..
To go around your problem you may think about extending you CI_Form_validation class with a My_form_validation..
class MY_Form_validation extends CI_Form_validation
{
public function __construct()
{
parent::__construct();
}
function username_check($str)
{
/* your code */
}
}
Then in your validation you must set only..
'rules' => 'required|username_check'

Private function can only be accessed by an object of the class. This function are visible in its own class only. Read more about variable/function scope here

Related

codeIgniter can't access session in Hook

I have created on hook to set current visiting URL to session. I have to use this URL later on. I have called session method of codeIgniter using $this->CI =& get_instance(); and then $this->CI->session->userdata but it is giving
Trying to get property of non-object on $this->CI->session->userdata line
I have done following things to enable hooks in CI
config.php
$config['enable_hooks'] = TRUE;
hooks.php
$hook['pre_controller'] = array(
'class' => 'Preclass',
'function' => 'checkreq',
'filename' => 'preclass.php',
'filepath' => 'hooks',
'params' => array()
);
preclass.php
class Preclass
{
private $CI;
public function __construct()
{
$this->CI =& get_instance();
}
public function checkreq($value='')
{
var_dump($this->CI->session->userdata);
die;
}
}
Note: Don't close this post as Duplicate of PHP errors. As I know about errors. This is in CodeIgniter and I want to check session before any controller method gets invoked.
From comment: "But I want it before controller methods invoke even before constructor"
To solve your issue, this is about the best you can do:
Make an MY_Controller.php in application/core:
class MY_Controller extends CI_Controller {
public function __construct() {
parent::__construct();
// class is just an alias for the controller name
if (!$this->user->is_allowed($this->router->class)) {
redirect('somepage');
}
}
}
Then have all your controllers extend MY_Controller:
class Somecontroller extends MY_Controller {
public function __construct() {
parent::__construct();
// nothing below the above line will be reached
// if the user isn't allowed
}
}
Whether or not you have a __construct() method in the class: nothing will happen so long as the user isn't allowed to access the page e.g. nothing after parent::__construct() will be called - even methods. Again, the fact that the parent constructor is called is implied if no constructor exists for the controller.
Note: if you autoload a model and do the same logic in the MY_Controller in the models __construct() the same results should be achieved. I just find this method cleaner.
This is not possible in Codeigniter as session itself a library and you are trying to call it pre_controller. When controllers not loaded yet how can you use it even in hook.
Solution
You may use post_controller_constructor instead what are using now
$hook['post_controller_constructor'] = array(
'class' => 'Preclass',
'function' => 'checkreq',
'filename' => 'preclass.php',
'filepath' => 'hooks',
'params' => array()
);
otherwise you may also use native session here
hope it help

Writing tests for controllers with forms

Ever since I started using Zend Framework 3, I had problems with testing my controllers. I'm trying to test my controllers with PhpUnit 5.7 and my controllers depend on Zend Form, which is hydrated with Doctrine's DoctrineObject.
I'm trying to put this as simple as possible, so here's a minimal example of a setup that's giving me headaches:
Controller:
class IndexController extends AbstractActionController {
private $form;
public function __construct(AlbumForm $form) {
$this->form = $form;
}
public function indexAction() {
return ['form' => $this-form];
}
}
ControllerFactory:
class IndexControllerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, ...) {
$formManager = $container->get('FormElementManager');
return new IndexController($formManager->get(AlbumForm::class));
}
}
The corresponding view template in albums/index/index.phtml:
<?php
$this->form->prepare();
$this->form->setAttribute('action', $this->url(null, [], true));
$albumFieldset = $this->form->get('album');
?>
<?= $this->form()->openTag($this-form) ?>
<div class="form-group">
<?= $this->formRow($albumFieldset->get('name')) ?>
</div>
<?= $this->form()->closeTag() ?>
The form:
class AlbumForm extends Form {
public function init() {
$this->add([
'name' => 'albumFieldset',
'type' => AlbumFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
}
}
The fieldset:
class AlbumFieldset extends Fieldset {
public function init() {
$this->add([
'name' => 'name',
'type' => Text::class,
'options' => [
'label' => 'Name of album',
],
]);
}
}
The FieldsetFactory:
class AlbumFieldsetFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, ...) {
$objectManager = $container->get(ObjectManager::class);
$fieldset = new AlbumFieldset();
$fieldset->setHydrator(new DoctrineObject($objectManager));
$fieldset->setObject(new Album());
return $fieldset;
}
}
Now, so far everything is working great.
However, when writing tests for this I run into troubles. Let me first show you what I have so far:
class IndexControllerTest extends AbstractHttpControllerTestCase {
protected function setUp() {
parent::setUp();
$this->configureServiceManager($this->getApplicationServiceLocator());
}
private function configureServiceManager(ServiceManager $services) {
$services->setAllowOverride(true);
$services->setService(ObjectManager::class, $this->mockObjectManager()->reveal());
$services->setService('FormElementManager', $this->mockFormManager()->reveal());
$services->setAllowOverride(false);
}
private $objectManager;
private function mockObjectManager() {
$this->objectManager = $this->prophesize(ObjectManager::class);
return $this->objectManager;
}
private $formManager;
private function mockFormManager() {
$this->formManager = $this->prophesize(FormElementManager::class);
$this->formManager->get(AlbumForm::class)->willReturn($this->mockForm()->reveal());
return $this->formManager;
}
private $form;
private function mockForm() {
$this->form = $this->prophesize(AlbumForm::class);
$this->form->prepare()->willReturn(null);
$this->form->setAttribute('action', Argument::type('string'))->willReturn(null);
$this->form->getAttributes()->willReturn([]);
$this->form->get('album')->willReturn($this->mockAlbumFieldset()->reveal());
return $this->form;
}
private $albumFieldset;
private function mockAlbumFieldset() {
$this->albumFieldset = $this->prophesize(AlbumFieldset::class);
$this->albumFieldset->get('name')->willReturn($this->mockName()->reveal());
return $this->albumFieldset;
}
private $name;
private function mockName() {
$this->name = $this->prophesize(Text::class);
$this->name->getLabel()->willReturn('label');
$this->name->getLabelAttributes()->willReturn(['for' => 'name']);
$this->name->getLabelOption('disable_html_escape')->willReturn(false);
$this->name->getLabelOption('always_wrap')->willReturn(false);
$this->name->getLabelOption('label_position')->willReturn('prepend');
$this->name->getName('album[name]');
$this->name->getAttribute('type')->willReturn('text');
$this->name->hasAttribute('id')->willReturn(true);
$this->name->getAttribute('id')->willReturn('name');
$this->name->getAttributes([])->willReturn([]);
$this->name->getValue()->willReturn(null);
$this->name->getMessages()->willReturn([]);
return $this->name;
}
}
This will eventually run without errors. However, I would like to draw your attention to the last few methods, especially mockName(). Most of those definitions are totally default and almost none of them are specified in AlbumFieldset in the beginning (only name is). It is very annoying to write them down for every form input I may have and writing this down actually introduces more errors than it solves. For example, I'm still not sure what the correct label option for always_wrap would be. I actually don't even care about that option, but I have to write something about it in my test, because otherwise the test fails with 'Prophecy\Exception\Call\UnexpectedCallException' with message 'Method call: - getLabelOption("always_wrap") on Double\Zend\Form\Element\Text\P245 was not expected, expected calls were: ....
Therefore, I'm asking you: is there any better way to go about this? A way that does not involve writing 20+ rows for every field I have in my fieldset. If it involves rewriting my controllers/fieldsets/view templates (etc.), that would totally be fine!
Any help is greatly appreciated! Also, this is my very first time asking something in a forum in over eight years of programming, so please bear with me if anything is unclear.
Yours
Steffen
PS: What I have already tried is to give the IndexController null instead of an actual form and simply abort the view template when it detects that the form is null. However, while that worked without that much setup, I was basically just avoiding the view template's logic. Because of that, I was not able to detect errors in the view template. That's not what I want.
edit IndexControllerTest: Change private as protected elsewhere and extends it for your new fields. Each new controller must overwrite methods calling parent::methodname($args) and add the needed code...

Can't initialize my plugin function in ZF2 constructor

I am quite new to ZF2 and I am preparing a demo application with simple login and CRUD system. Now for login I have prepared a plugin which consists of some functions that will authenticate users, return the logged in user data, return the logged in status etc. But the problem that I am facing is I can't initialize any variable into the constructor of my controller which will store any return value from the plugin. It's always showing service not found exception.
Please find my plugin code below:
AuthenticationPlugin.php
<?php
namespace Album\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\Session\Container as SessionContainer;
use Zend\View\Model\ViewModel;
use Album\Entity\User;
class AuthenticationPlugin extends AbstractPlugin{
protected $entityManager;
protected $usersession;
public function __construct(){
$this->usersession = new SessionContainer('UserSession');
}
public function dologin($email,$password)
{
$getData = $this->em()->getRepository('Album\Entity\User')->findOneBy(array('email' => $email, 'password' => $password));
if(count($getData)){
$this->usersession->offsetSet('userid', $getData->getId());
return true;
}
else{
return false;
}
}
public function isloggedin(){
$userid = $this->usersession->offsetGet('userid');
if(!empty($userid)){
return true;
}
else{
return false;
}
}
public function logindata(){
$userid = $this->usersession->offsetGet('userid');
$getData = $this->em()->getRepository('Album\Entity\User')->findOneBy(array('id' => $userid));
return $getData;
}
public function logout(){
$this->usersession->offsetUnset('userid');
}
public function em(){
return $this->entityManager = $this->getController()->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
}
?>
In my module.config.php
'controller_plugins' => array(
'invokables' => array(
'AuthPlugin' => 'Album\Controller\Plugin\AuthenticationPlugin',
)
),
Now I am doing this in my controller:
protected $entityManager;
protected $isloggedin;
protected $authentication;
public function __construct(){
$this->authentication = $this->AuthPlugin();
$this->isloggedin = $this->authentication->isloggedin();
}
The error I am getting is like below:
An error occurred An error occurred during execution; please try again
later. Additional information:
Zend\ServiceManager\Exception\ServiceNotFoundException
File:
D:\xampp\htdocs\subhasis\zf2-tutorial\vendor\zendframework\zendframework\library\Zend\ServiceManager\ServiceManager.php:555
Message:
Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for AuthPlugin
But if I write the above constructor code in any of my controller actions everything is fine. in ZF1 I could initialize any variable in the init() method and could use the variable in any of my actions. How can I do this in ZF2? Here, I want to detect if the user is logged in the constructor itself. Now I have to call the plugin in every action which I don't want.
What should I do here?
The error you are receiving is because you are trying to use the ServiceManager (via the Zend\Mvc\Controller\PluginManager) in the __construct method of the controller.
When a controller is registered as an invokable class, the Service Manager (ControllerManager) is responsible for the creating the controller instance. Once created, it will then call the controllers various default 'initializers' which also inlcudes the plugin manager. By having your code in __construct it is trying to use the plugin manager before it has been set.
You can resolve this by using a controller factory, rather than an invokable in module.config.php.
'controllers' => [
'factories' => [
'MyModule\Controller\Foo' => 'MyModule\Controller\FooControllerFactory',
],
],
Then the factory
namespace MyModule\Controller\FooControllerFactory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class FooControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $controllerManager)
{
$serviceManager = $controllerManager->getServiceLocator();
$controllerPluginManager = $serviceManager->get('ControllerPluginManager');
$authPlugin = $controllerPluginManager->get('AuthPlugin');
return new FooController($authPlugin);
}
}
Lastly, update the controller __construct to add the new argument and remove the call to $this->authPlugin()
class FooController extends AbstractActionController
{
public function __construct(AuthPlugin $authentication)
{
$this->authentication = $authentication;
$this->isloggedin = $authentication->isloggedin();
}
}

Yii Validate an un-bound Variable (non-stored)

Classic problem:
verify that a user accepted the contract terms but the value of the acceptance is not stored (bound) in the database...
Extend CFormModel rather than CActiveForm (because CActiveForm binds
values to DB)
Post a CFormModel to a controller action
Validate a CFormModel
I'm asking this question to answer it because the existing questions end in see the documentation...
extend CFormModle, define the rules and got to validate. With bound variables you validated as part of save. Now you validate() by itself but Validate requires a list of attributes which is not defined in CFormModel. So, what do you do? You do this:
$contract->validate($contract->attributeNames())
Here's the full example:
class Contract extends CFormModel
{
...
public $agree = false;
...
public function rules()
{
return array(
array('agree', 'required', 'requiredValue' => 1, 'message' => 'You must accept term to use our service'),
);
}
public function attributeLabels()
{
return array(
'agree'=>' I accept the contract terms'
);
}
}
Then in the controller you do this:
public function actionAgree(){
$contract = new Contract;
if(isset($_POST['Contract'])){
//$contract->attributes=$_POST['Contract']; //contract attributes not defined in CFormModel
...
$contract->agree = $_POST['Contract']['agree'];
...
}
if(!$contract->validate($contract->attributeNames())){
//re-render the form here and it will show up with validation errors marked!
}
The results:

Passing properties from a child object to the parent PHP

I've worked with cakePHP in the past and liked the way they built their model system. I want to incorporate their idea of handling validation between extended models.
Here is an example:
class users extends model {
var $validation = array(
"username" => array(
"rule" => "not_empty"
),
"password" => array(
"rule" => "valid_password"
)
);
public function create_user() {
if($this->insert() == true) {
return true;
}
}
}
class model {
public function insert() {
if(isset($this->validation)) {
// Do some validation checks before we insert the value in the database
}
// Continue with the insert in the database
}
}
The problem with the this is that model has no way of getting the validation rules as it's the parent class. Is there a way I can pass the $validation property to the parent class without explicitely passing the validation rules through say the create_user() method as a parameter?
EDIT:
Also, avoiding passing it via the __construct() method to the parent class. Is there another way of doing this which would not cause a lot of extra code within my users class but get the model class to do most of the work (if not all?)
If the instance is a $user, you can simply refer to $this->validation in model::insert().
It would seem that model should also be abstract in this case, preventing instantiation and perhaps confusion.
Create a new abstract method in the model class named: isValid() that each derived class will have to implement, then call that method during the insert() function.
model class:
class model {
abstract protected function isValid();
public function insert() {
if($this->isValid())) { // calls concrete validation function
}
// Continue with the insert in the database
}
}
user class:
class users extends model {
var $validation = array(
"username" => array(
"rule" => "not_empty"
),
"password" => array(
"rule" => "valid_password"
)
);
protected function isValid() {
// perform validation here
foreach ($this->validation) { //return false once failed }
return true;
}
public function create_user() {
if($this->insert() == true) {
return true;
}
}
}

Categories