Making functions within a class DRY in PHP - php

I'm using CakePHP and have created a class as follows:
class ApiController extends AppController {
// functions
}
I have about 10 functions in the class and I have found that I have repeated myself with the exact 3 same lines of code at the beginning of every function:
if ($this->request->is('post')) {
$data = $this->request->input('json_decode',true);
$authUser = explode('.',$_SERVER['PHP_AUTH_USER']);
$location_id = $authUser[1];
// Rest of my function
}
Is there any way that I can create something in the class which runs those 3 lines of code first, and then makes the $data and $location_id variables available for my functions to use, or must I write those 3 lines for every function?

It can be done using private method.
private $data = null;
private $locationId = null;
public function __construct($request = null, $response = null) {
parent::__construct($request = null, $response = null);
$this->data = $this->request->input('json_decode',true);
$authUser = explode('.',$_SERVER['PHP_AUTH_USER']);
$this->locationId = $authUser[1];
}
and then use it like this
$this->locationId;

You can write a method and put the 2 variables as a property of the class.
e.g.
class ApiController {
private $location_id;
private $data;
private function init() {
// ...
}
}
And then access the variables by doing $this->location_id.

Related

How to reach doctrine repository in private (non-route) function in symfony5?

When I need to access an entity repository in a public function I usually inject it with the args for that function, such as
public function showAction(DebitPeriod $debitPeriod, ExtraOpeningRepository $extraOpeningRepository)
However, when I have a private function (which is only serving as a sub-function to one of the controller routes) how can I reach the entity repository from there?
I want to be able to use it like this
use App\Repositories\ExtraOpeningRepository;
private function assembleEntries($units,$day = null) {
$eor = new ExtraOpeningRepository;
}
or like this
private function assembleEntries($units,$day = null, ExtraOpeningRepository $extraOpeningRepository) {
The error I get is this:
Too few arguments to function
App\Repository\ExtraOpeningRepository::__construct(), 0 passed
I don't want to have to chain it onward all the way from the route controller like this:
public function showAction(DebitPeriod $debitPeriod, ExtraOpeningRepository $extraOpeningRepository) {
$units = 1;
$dataset = $this->assembleEntries($units,null,$extraOpeningRepository);
}
private function assembleEntries($units,$day = null, ExtraOpeningRepository $extraOpeningRepository) {
//do stuff
}
IS there a way to achieve it cleaner, without the mess of the above example?
As per Cerad's comment, this is the successful result =)
use App\Repository\ExtraOpeningRepository;
class EntryController extends AbstractController
{
private $extraOpeningRepository;
public function __construct(ExtraOpeningRepository $extraOpeningRepository)
{
$this->extraOpeningRepository = $extraOpeningRepository;
}
private function assembleEntries($units,$day = null)
{
$extraOpenings = $this->extraOpeningRepository->findForDay($output[$i]['dateToFetch']);

Get variables of parent class php from Ajax

I've the followings classes:
abstract class utility, which contains general functions (e.g. database connection)
class get_conf, which returns the main variables that I will need almost everywhere (e.g. language, login status)
class get_data to handle the query to the database and return the results.
This is in my index file:
$init = new get_conf();
$lang = $init->lang;
$status = $init->status;
(...)
$config = new get_data($lang,$status);
This is the construct of class get_data.
class get_data extends utility {
public function __construct($lang = NULL,$status = NULL) {
$this->lang = $lang;
$this->status = $status;
}
...
Everything work fine, but I don't know how to handle at best during an ajax call.
After instantiate the class that I need,
$config = new get_data();
What is the best way to get $lang and $status? At the moment I'm calling again the functions that define their values - get_language(), check_login().
But there is a better way? Should I use sessions? It doesn't sound good to me call every time those function, especially when I have multiple ajax calls in the same page.
EDIT: I'm sorry, is my fault cause I formulated the question in the wrong way. What I would need is to get the variables from Ajax and ok, but I would need to use them in the class
For example, in the class get_data I've this function:
public function get_category($id_cat) {
$q = "SELECT category FROM p_category WHERE id = '".$id_cat."' AND code = ".$this->lang.";
return $this->exe_query($q);
}
Cause I'm calling this function both from Ajax and not, depending on the situation, $this->lang is defined only when I call it out of an Ajax request. And even using static var doesn't work with Ajax.
Write the following member function inside get_data class:
public function getCVar($var){
$c = new get_data();
eval('$tmp = $c->'.strtolower($var).';');
return $tmp;
}
Then you can get the variables value like below:
echo get_data::getCVar('lang');
echo get_data::getCVar('status');
Try it:
class get_data extends utility {
static $lang = null;
static $status = null;
public function __construct($lang = NULL,$status = NULL) {
if($lang !== null){
self::$lang = $lang;
}
if($status !== null){
self::$status = $status;
}
}
}
$config = new get_data($lang,$status);
echo get_data::$status;
echo get_data::$lang;

Injecting single-use object into class

I have the following code:
<?php
class X
{
public function do($url)
{
$httpRequest = new \HttpRequest\Curl($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
In order to be able to unit test this class, I'd like to inject a mocked HttpRequest class. One way to do this would be as follows:
<?php
class X
{
private $httpRequestClass;
public function __construct($httpRequestClass = '\HttpRequest\Curl')
{
$this->httpRequestClass = $httpRequestClass;
}
public function do($url)
{
$httpRequest = new $this->httpRequestClass($url, $this->getOptions());
$httpRequest->fire();
// etc.
}
// ...
}
But this doesn't seem right. Any other ideas?
public function __construct($url, $httpRequestClass = null)
{
$this->url = $url;
if ($httpRequestClass == null) //> Default
$this->httpRequestClass = new HttpRequest\Curl($this->url);
else
$this->httpRequestClass = $httpRequestClass;
}
so when you are using this class normally just call it with one param
yourClass('your url');
Otherwise pass the istance in the second argument
yourClass('url', new MockedObj);
Of course you should always Inject your dependencies without providing a default object
The class needs to generate objects of type HttpRequest, but we don't necessarily want it to initialize an object: we may want it to use the prototype pattern, for example. Therefore, the class calls for the factory pattern. I chose a factory callback, as opposed to a factory class, for brevity.
<?php
class X
{
private $factoryCallback;
public function __construct($factoryCallback = null)
{
$this->factoryCallback = $factoryCallback;
}
public function do($url)
{
$httpRequest = $this->createHttpRequest($url);
$httpRequest->fire();
// etc.
}
private function createHttpRequest($url)
{
$callback = $this->factoryCallback;
if (is_callable($callback)) {
return $callback($url, $this->getOptions());
}
return new \HttpRequest\Curl($url, $this->getOptions());
}
// ...
}
The helper method, createHttpRequest(), is a bit redundant in this example, but would be used for error handling in production code.

php oop constructor

OK. here is what I'm trying to do:
class Image{
public $_image;
public $_extension;
public $_mime;
public $_size;
public $_location;
public $_description;
public function __construct($image, $location){
$this->_image = $image;
$this->_location = $location;
$this->_extension = getExtension();
$this->_mime = getMime();
$this->_size = getSize();
}
private functions fallow.....
}
But I keep getting an internal server error when I try to run it. When I comment out the method calls it works. So the question is can I call methods from inside the constructor or am I doing something wrong with the methods.
Do your functions getExtension, getMime and getSize exist? Are they methods on this class? If they are methods, they need to be called with $this->... as in
$this->_extension = $this->getExtension();
If they are not methods, and are functions, you need to make sure the files that contain/define them are loaded before you run the constructor.
Well ..this fragment of code will work as expected:
class Foo
{
protected $secret = null;
public function __construct( $data )
{
$this->secret = $this->makeSecret($data);
}
public function makeSecret( $data )
{
return md5( $data );
}
}
$bar = new Foo( 'lorem ipsum' );
That is not a problem.
But you should know, that is considered to be a bad practice - to do computation/work in the constructor. It makes that class practically untestable. Instead, if you need to perform some computation before "releasing" the object to the rest of the code, you should use a factory. Something along the lines of :
class ImageFactory
{
public function build($image, $location)
{
$instance = new Image($image, $location);
$instance->prepare();
return $instance;
}
}
The class would need some changes:
class Image
{
protected $_image; // you were leaking abstraction
protected $_extension;
protected $_mime;
protected $_size;
protected $_location;
protected $_description;
public function __construct($image, $location)
{
$this->_image = $image;
$this->_location = $location;
}
public function prepare()
{
$this->_extension = $this->getExtension();
$this->_mime = $this->getMime();
$this->_size = $this->getSize();
}
private functions fallow.....
}
Now when you need to create new object you do:
$factory = new ImageFactory;
$image = $factory->build( $file, '/uploads/' );
Of course the instance of ImageFactory can be reusable, and if all your images use the same $location, then you would pass that variable to factory at the initialization. And the factory would be able to "remember it" and pass to all the images it creates:
$factory = new ImageFactory('/uploads/');
$img1 = $factory->build( $file );
$img2 = $factory->build( $something_else );
This is actually how one should deal with creating multiple objects, which all need access to same DB connection instance.
Yes, you can call methods from within the constructor. Remember that the __construct() magic method was implemented in PHP 5. Prior to that, you created a function named the same as your class which acted as your constructor so depending on your PHP version, that could be a problem.
Additionally, the function calls you are making, are they in the class or external? If they are inside the class you need to call them this way:
$this->_extension = $this->getExtension();
You didnt specified what error you are expiriencing clearly. But try calling you class methods even inside the class using this keyword, otherwise it would not work:
public function __construct($image, $location)
{
$this->_image = $image;
$this->_location = $location;
$this->_extension = $this->getExtension();
$this->_mime = $this->getMime();
$this->_size = $this->getSize();
}
Would be a better idea to post your code for the methods you wrote. There could be something wrong within them as well. Possibly forgetting to return a result or something...

Zend_Db_Table_Row_Abstract Extended Row Class Not Functioning

I'm using the Zend_Db framework, and I've run into a snag. I'm trying to add custom handling to the column naming at the row level, but it's failing to invoke my function for some reason.
I've stripped down the problem to simply try and figure out if the underlying "Row" class is ever even created. From what I can tell, it isn't.
Here's what I've got:
// this class functions correctly; I get "table" written to my output
class DHR_Table extends Zend_Db_Table_Abstract
{
protected $_rowClass = 'DHR_Row';
function __construct(){
echo "table";
parent::__construct();
}
}
// this class never gets called, at least not that is evident from the constructor echo
class DHR_Row extends Zend_Db_Table_Row_Abstract
{
protected $inflector = null;
function __construct(){
echo "row";
parent::__construct();
}
}
// this is the actual implementation class that uses these two:
class Application_Model_DbTable_Applicants extends DHR_Table
{
protected $_name = 'applicants';
}
My output includes some data (excluded from this post, but part of the "Applicants" class) and "table", but no "row". Any ideas why this might be happening? Version 1.11.11 of the Zend framework.
[Edit]
Here's the usage:
class ApplicantsController extends DHR_RestController
{
public function indexAction()
{
$applicants = new Application_Model_DbTable_Applicants();
$result = $applicants->fetchAll();
$this->success($result);
}
protected function success($data, $code = 200)
{
if(is_a($data, 'Zend_Db_Table_Rowset')){
// we could do some pagination work here
$data = $data->toArray();
}
$this->setResponseCode($code)->appendBody(Zend_Json::encode(array(
'success'=>true,
'data' => $data
)));
}
}
I would expect to at least have some method on the row class invoked when returning the serialized results...
[Update]
If I use "fetchRow" everything works as expected; fetchAll simply does't do the conversion to the underlying object type.
I was just looking at the code for the row/abstract class.
Try setting a value for $_tableClass. $_tableClass = 'DHR_Table';
I'm afraid that won't work as it looks like Zend/Db/Table/Row/Abstract.php is going to look for a table definition no matter what, so the level of abstraction you seem to be after may not be possible without further extending.
//excerpt from __construct Zend/Db/Table/Row/Abstract.php
public function __construct(array $config = array())
{
if (isset($config['table']) && $config['table'] instanceof Zend_Db_Table_Abstract) {
$this->_table = $config['table'];
$this->_tableClass = get_class($this->_table);
} elseif ($this->_tableClass !== null) {
$this->_table = $this->_getTableFromString($this->_tableClass);
}
// cont...
// Retrieve primary keys from table schema
if (($table = $this->_getTable())) {
$info = $table->info();
$this->_primary = (array) $info['primary'];
}
$this->init();

Categories