Access value in method sent by parent::__construct() - php

I'm having trouble getting the value passed via parent__construct in a Controller's child class and using that value in a class called View.
When performing a var_dump in the __Construct of the Controller class I can retrieve the value sent from the Form class, but when I try to retrieve this same value in a METHOD inside the View class, it appears as null.
Could you help me with this problem?
Here are the codes:
============================================
class Form extends Controller
{
//General builder of the WEB pages
public function __construct()
{
//Redirecting the entire site for maintenance
//redirect("/ops/maintenance");
parent::__construct(__DIR__ . "/../../../themes/" . THEME . "/pages/");
}
}
============================================
class controller
{
/** #var View */
protected $view;
/** #var Message */
protected $message;
//Builder extending classes
/**
* Controller constructor
* #param string|null $pathToViews
*/
public function __construct(string $pathToViews = null)
{
$this->view = new View($pathToViews);
//the value passed in parent::__construct() appears here
var_dump($pathToViews);
$this->message = new Message();
}
}
============================================
//Class responsible for handling Views requests
class view
{
protected $pathToViews;
public function __construct(string $pathToViews = null)
{
$this->pathToViews = $pathToViews;
}
//Function to Load the View and send content from the backend to the frontend
public function show($viewName, $data = [])
{
var_dump($this->pathToViews);
}
}

Update:
If you don't want to call the show method in the controller class, but you want to instantiate that class in your controller class you should find a way to get the view class accessible outside of your controller class. Just instantiating view class again outside of the controller is another instance and will indeed show a null value on the $pathToViews variable.
One way of doing this is like this:
class controller
{
/** #var View */
protected $view;
//Builder extending classes
/**
* Controller constructor
* #param string|null $pathToViews
*/
public function __construct(string $pathToViews = null)
{
$this->view = new View($pathToViews);
var_dump($pathToViews);
}
public function getView() : View
{
return $this->view;
}
}
//Class responsible for handling Views requests
class View
{
protected $pathToViews;
public function __construct(string $pathToViews = null)
{
$this->pathToViews = $pathToViews;
echo $this->pathToViews;
}
//Function to Load the View and send content from the backend to the frontend
public function show($viewName, $data = [])
{
var_dump($this->pathToViews);
}
}
$controller = new controller('testString');
$view = $controller->getView();
$view->show('test');
Another way of doing this is using the principle of dependency inversion
class controller
{
/** #var View */
protected $view;
//Builder extending classes
/**
* Controller constructor
* #param View $view
* #param string|null $pathToViews
*/
public function __construct(View $view, string $pathToViews = null)
{
$this->view = $view;
$this->view->setPathtoViews($pathToViews);
var_dump($pathToViews);
}
public function getView() : View
{
return $this->view;
}
}
//Class responsible for handling Views requests
class View
{
protected $pathToViews;
public function setPathtoViews(string $pathToViews)
{
$this->pathToViews = $pathToViews;
}
//Function to Load the View and send content from the backend to the frontend
public function show($viewName, $data = [])
{
var_dump($this->pathToViews);
}
}
$view = new View();
$controller = new controller($view, 'testString');
$view->show('test');
?>
Original answer
Your code runs fine on my machine. I have run the code block below. What I did change to make it work was actually calling $this->view->show(). In your code example that wasn't the case.
As a small note. I know php is case insensitive but as a good practice call your classes with the same casing as how you defined them (view vs View).
<?php
class controller
{
/** #var View */
protected $view;
//Builder extending classes
/**
* Controller constructor
* #param string|null $pathToViews
*/
public function __construct(string $pathToViews = null)
{
$this->view = new View($pathToViews);
$this->view->show('test');
var_dump($pathToViews);
}
}
//Class responsible for handling Views requests
class View
{
protected $pathToViews;
public function __construct(string $pathToViews = null)
{
$this->pathToViews = $pathToViews;
echo $this->pathToViews;
}
//Function to Load the View and send content from the backend to the frontend
public function show($viewName, $data = [])
{
var_dump($this->pathToViews);
}
}
$controller = new controller('testString');
?>

Related

Call to a member function on null, phone validation service

PHP with Symfony framework:
First of all before the context:
My input form is being built by form builder. Nothing is wrong there. So that is not the problem
I am making a sms validator system. I have a controller, and 2 services(validatorservice, smsapi(for api call)) Now my validatorservice looks like this:
class ValidatorService
{
public function validate($telefoonnummer)
{
$pregpatternNL = '(^\+[0-9]{2}|^\+[0-9]{2}\(0\)|^\(\+[0-9]{2}\)\(0\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\-\s]{10}$)';
if (!preg_match($pregpatternNL, $telefoonnummer)) {
return false;
} else {
return true;
}
}
}
Then my homecontroller:
use App\Service\ValidatorService;
class HomeController extends AbstractController
{
/** #var SmsApi */
private $smsApi;
/** #var validatorService */
private $validatorService;
public function __construct1(SmsApi $smsApi, Validatorservice
$validatorService)
{
$this->smsApi = $smsApi;
$this->validatorService = $validatorService;
}
/**
* #Route("/")
* #Template()
*
* #param Request $request
*
* #return array
*/
public function indexAction(Request $request)
{
$form = $this->createForm(
SmsLogFormType::class,
new SmsLog(),
[
'method' => 'post',
]
);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** #var SmsLog $smslog */
$formData = $form->getData();
try {
$telefoonnummer = $formData->getTel();
$telefoonnummer = preg_replace('/[^0-9]/', '', $telefoonnummer);
$validatorservices = $this->validatorService-
>validate($telefoonnummer);
if ($validatorserviceres === false) {
$this->addFlash(
'notice',
'telefoonnummer onjuist formaat'
);
exit;
} else {
blablabla
}
Now whatever i try i get the error :
Call to a member function validate() on null
At first i thought maybe its something with the constructor names, but found online that that doesn't matter at all (also i didn't receive any code errors there)
Then i tried adding echo's to the if statement in my service. Maybe return true or false is seen as null but this doesn't work either.
I guess it's because of the number of arguments per constructor. If you define multiple constructors for a class, they should have different argument counts.
What you could do instead is to check whether or not the object you received is part of the wanted class/classes.
Or create static functions that instatiate the class with different object types.
EDIT
Use the default autowiring mechanisms:
private $smsApi;
private $validatorService;
public function __construct(SmsApi $smsApi, ValidatorService $validatorService)
{
$this->smsApi = $smsApi;
$this->validatorService = $validatorService;
}
It should work as intended if you change your Code to this :
/** #var SmsApi */
private $smsApi;
private $validatorService;
public function __construct(SmsApi $smsApi, ValidatorService $validatorService)
{
$this->validatorService = $validatorService;
$this->smsApi = $smsApi;
}
__construct1 and __construct2 are not native functions of php, so when the class is loaded, the constructors are not invoking and validatorService/smsApi are not being set (so they are null). The native function is called __construct.
/** #var SmsApi */
private $smsApi;
private $validatorService;
public function __construct(SmsApi $smsApi, ValidatorService $validatorService)
{
$this->smsApi = $smsApi;
$this->validatorService = $validatorService;
}
Or if doest not work, inject the services as arg in
public function indexAction(Request $request)
so...
public function indexAction(Request $request,SmsApi $smsApi, ValidatorService $validatorService)
and use $validatorService->validate();

Code completion in injected classes

Here is my sample code which is what I want to get, but in User class I get no codecompletion (for $this->app) in PHPSotrm.
How to change that code to enable code completion? I want to avoid global.
namespace myApp
class app
{
public $pdo = "my PDO";
public function __construct() {
$this->user=new User($this);
$this->test=new Test($this);
echo $this->user->getPDO();
//"my PDO"
echo $this->test->getUserPDO();
//"my PDO"
}
}
class User
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getPDO() {
return $this->app->pdo;
//no code completion
}
}
class Test
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getUserPDO() {
return $this->app->user->getPDO;
//no code completion
}
}
There's plenty of information on how to achieve this. All you need to do is to add the PHPDoc describing the property type.
class User
{
/**
* #var App
*/
private $app=null;
/**
* #param App $app
*/
public function __construct($app) {
$this->app = $app;
}
/**
* #return PDO
*/
public function getPDO() {
return $this->app->pdo;
}
}
If properties are implemented via magic getters / setters, you can use the same principles on the class itself.
/**
* #property App $app
* #method Pdo getPdo()
*/
class User
{
// …

PHP classes and functions/ undefined variable

Why do I get undefined property Takeover::user2 on function takeover?
I'm not sure what I'm doing wrong. Can someone help?
I can call user2->addsaldo() on main file but I can't call it inside another function. Why?
Class user
class User {
/**
* #AttributeType int
*/
private $iduser;
/**
* #AttributeType float
*/
private $saldo=0;
/**
* #AssociationType Portefolio
* #AssociationKind Composition
*/
public $idportefolio;
public function __construct($iduser){
$this->iduser = $iduser;
}
/**
* #access public
*/
public function getid() {
// Not yet implemented
}
/**
* #access public
*/
public function addsaldo($saldo) {
$this->saldo = $saldo;
}
}
Class takeover
class Takeover {
/**
* #AttributeType int
*/
private $idTakeover;
/**
* #AssociationType root
* #AssociationMultiplicity 1
*/
public $Root;
public $IdeasTakerover=array();
public function __construct($idTakeover){
$this->idTakeover = $idTakeover;
}
/**
* #access public
*/
public function GetIdCompraRoot() {
// Not yet implemented
}
public function AddIdeasTakeover($idTakeover, $idideia) {
$this->idTakeover = $idTakeover;
$this->idideia = $idideia;
array_push($this->IdeasTakerover,$idideia);
}
/**
* #access public
*/
public function Takeover() {
$this->user2->addsaldo(200); //USER2 DOES EXIST
}
}
Creating users and calling them:
$takeover = new Takeover(1);
for ($i=0; $i<$conta; $i++ ){
$takeover->AddIdeasTakeover(1,$idsideias[$i]);
}
$takeover->Takeover();
if ($partial == "user") {
$booleanUser = TRUE;
$iduser=substr($buffer, 4);
${'user'.$iduser} = new User($iduser);
}
The problem is not in this class. The problem is that $GLOBALS['user2'] is not defined when referenced here: $GLOBALS['user'.$this->IdeiasTakeover[$i]]. You then call addsaldo() on an undefined array element in $GLOBALS.
On another note, it is impossible to write good code using $GLOBALS. Global variables are bad news. You should not be using/referencing global variables. The exception would be at a low level when referencing $_POST, $_GET, etc. Even still, all the good PHP frameworks wrap these in a request object.
Edit
Dependency Injection is a better way for one class to use another:
class X {
$yInstance;
public function __construct($yInstance)
{
$this->yInstance = $yInstance;
}
public function x()
{
//Call your 'y' method on an instance of Y
$this->yInstance->y();
}
}
class Y {
public function y()
{
echo 'Y::y() called!';
}
}
Call X::x()
$y = new Y();
$x = new X($y);
$x->x();
Output:
Y::y() called!
The easiest way to do this is to use a dependency injection container. Here is my favorite one for PHP: http://symfony.com/doc/current/components/dependency_injection/introduction.html
Also, check out Martin Fowlers classic article about IOC:
http://martinfowler.com/articles/injection.html
Good luck!

Zend Framework 2 instantiate table in Model

I have a Model, called Admin with custom functions.
<?php
namespace ZendCustom\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\Exception\ErrorException;
abstract class Admin {
/**
* #var TableGateway
*/
protected $_table;
/**
* #var array
*/
protected $data;
/**
* #var bool
*/
protected $loaded = false;
/**
* Constructor
*
* #param array $data
* #throws Exception
*/
public function __construct(array $data = array()) {
if (!is_null($this->_table)) {
if (!empty($data)) {
$this->loaded = !empty($this->data);
}
} else {
die('Please, specify table for ' . __CLASS__);
}
}
}
And docs says to describe table, we should use:
// module/Album/src/Album/Controller/AlbumController.php:
public function getAlbumTable()
{
if (!$this->albumTable) {
$sm = $this->getServiceLocator();
$this->albumTable = $sm->get('Album\Model\AlbumTable');
}
return $this->albumTable;
}
http://framework.zend.com/manual/2.0/en/user-guide/database-and-models.html
How could I set my Model Table inside Admin Model, without Controller?
You can just inject it when it's instantiated via the Service Manager.
Module.php
/**
* Get the service Config
*
* #return array
*/
public function getServiceConfig()
{
return array(
'factories' => array(
'ZendCustom\Model\AdminSubclass' => function($sm) {
// obviously you will need to extend your Admin class
// as it's abstract and cant be instantiated directly
$model= new \ZendCustom\Model\AdminSublcass();
$model->setAlbumTable($sm->get('Album\Model\AlbumTable'));
return $model;
},
)
)
}
Admin.php
abstract class Admin {
protected $_albumTable;
/**
* #param \Album\Model\AlbumTable
*/
public function setAlbumTable($ablum)
{
this->_albumTable = $ablum;
}
}
Now if you want your Admin class (or a sublcass of it rather..) then you use the Service Manage to get the instance, and it will inject the Table Object you wanted..
Inside a controller you could do this:
$admin = $this->getServiceLocator()->get('ZendCustom\Model\AdminSubclass');

How can I pass extra parameters to the routeMatch object?

I'm trying to unit test a controller, but can't figure out how to pass some extra parameters to the routeMatch object.
I followed the posts from tomoram at http://devblog.x2k.co.uk/unit-testing-a-zend-framework-2-controller/ and http://devblog.x2k.co.uk/getting-the-servicemanager-into-the-test-environment-and-dependency-injection/, but when I try to dispatch a request to /album/edit/1, for instance, it throws the following exception:
Zend\Mvc\Exception\DomainException: Url plugin requires that controller event compose a router; none found
Here is my PHPUnit Bootstrap:
class Bootstrap
{
static $serviceManager;
static $di;
static public function go()
{
include 'init_autoloader.php';
$config = include 'config/application.config.php';
// append some testing configuration
$config['module_listener_options']['config_static_paths'] = array(getcwd() . '/config/test.config.php');
// append some module-specific testing configuration
if (file_exists(__DIR__ . '/config/test.config.php')) {
$moduleConfig = include __DIR__ . '/config/test.config.php';
array_unshift($config['module_listener_options']['config_static_paths'], $moduleConfig);
}
$serviceManager = Application::init($config)->getServiceManager();
self::$serviceManager = $serviceManager;
// Setup Di
$di = new Di();
$di->instanceManager()->addTypePreference('Zend\ServiceManager\ServiceLocatorInterface', 'Zend\ServiceManager\ServiceManager');
$di->instanceManager()->addTypePreference('Zend\EventManager\EventManagerInterface', 'Zend\EventManager\EventManager');
$di->instanceManager()->addTypePreference('Zend\EventManager\SharedEventManagerInterface', 'Zend\EventManager\SharedEventManager');
self::$di = $di;
}
static public function getServiceManager()
{
return self::$serviceManager;
}
static public function getDi()
{
return self::$di;
}
}
Bootstrap::go();
Basically, we are creating a Zend\Mvc\Application environment.
My PHPUnit_Framework_TestCase is enclosed in a custom class, which goes like this:
abstract class ControllerTestCase extends TestCase
{
/**
* The ActionController we are testing
*
* #var Zend\Mvc\Controller\AbstractActionController
*/
protected $controller;
/**
* A request object
*
* #var Zend\Http\Request
*/
protected $request;
/**
* A response object
*
* #var Zend\Http\Response
*/
protected $response;
/**
* The matched route for the controller
*
* #var Zend\Mvc\Router\RouteMatch
*/
protected $routeMatch;
/**
* An MVC event to be assigned to the controller
*
* #var Zend\Mvc\MvcEvent
*/
protected $event;
/**
* The Controller fully qualified domain name, so each ControllerTestCase can create an instance
* of the tested controller
*
* #var string
*/
protected $controllerFQDN;
/**
* The route to the controller, as defined in the configuration files
*
* #var string
*/
protected $controllerRoute;
public function setup()
{
parent::setup();
$di = \Bootstrap::getDi();
// Create a Controller and set some properties
$this->controller = $di->newInstance($this->controllerFQDN);
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => $this->controllerRoute));
$this->event = new MvcEvent();
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator(\Bootstrap::getServiceManager());
}
public function tearDown()
{
parent::tearDown();
unset($this->controller);
unset($this->request);
unset($this->routeMatch);
unset($this->event);
}
}
And we create a Controller instance and a Request with a RouteMatch.
The code for the test:
public function testEditActionWithGetRequest()
{
// Dispatch the edit action
$this->routeMatch->setParam('action', 'edit');
$this->routeMatch->setParam('id', $album->id);
$result = $this->controller->dispatch($this->request, $this->response);
// rest of the code isn't executed
}
I'm not sure what I'm missing here. Can it be any configuration for the testing bootstrap? Or should I pass the parameters in some other way? Or am I forgetting to instantiate something?
What I did to solve this problem was move the Application::init() and the configuration from the Bootstrap to the setUp() method. It takes a while to load, but it works.
My Bootstrap has only the code needed to configure the autoloader, while the setUp() method has something similar to the old Bootstrap::go() code.

Categories