I'm fairly new to Laravel and was wondering what the most efficient way of instantiating and making available multiple objects to my classes was.
An example of what I am doing currently would be, in kind of sudo code:
// MainController.php
<?php
Class MainController extends BaseController {
public function __construct() {
parent::construct();
}
// class code follows
}
// BaseContoller.php
<?php
use classFile;
Use repositoryFile;
use classFile2;
Use repositoryFile2;
..... and lets say 10 more "included" files
Class BaseController extends Controller {
var classFile;
var repositoryFile;
var classFile2;
var repositoryFile2;
..... and 10 more variables;
public function __construct() {
$this->classFile = new classFile;
$this->classFile = new classFile;
$this->repositoryFile = new repositoryFile;
$this->classFile2 = new classFile2;
$this->repositoryFile2 = new repositoryFile2;
..... and 10 more class instantiations
}
// class code follows
}
Hopefully that makes sense.....
Almost all of my classes that inherit BaseController will at some point make use of these objects so loading them all in for each class is I guess the right thing. My main two questions though are:
How expensive, from a pure code point of view, is instantiating multiple objects like this before they are needed. I have included them in the BaseController file because I found myself repeating these included definitions in the numerous files that inherit from BaseController.
From a Laravel point of view is there a better framework way of doing this kind of thing? Originally I had them being injected in through each classes constructor but again they were being repeated (the same definition in multiple files) and were becoming horribly large.
I've followed a lot of the Laracasts tuts as well as various reading material but I've not yet seen how people handle a large volume of objects like I'm trying to use. Or perhaps thats where I'm going "wrong"?
Cheers !
== Heres an example of the call for the homepage of the API I'm working on. It aggregates content from all across the site into a feed for a mobile app:
public function index()
{
$channels = $this->channelRepository->getChannels();
$allChannels = $this->channelRepository->getAllChannels();
$sponsors = $this->sponsorRepository->getSponsors();
$inactiveUserChannels = [];
// use the pattern maker, set the pattern we want to use
$this->patternMaker->setPattern(1);
$channelFeed = [];
if( userIsAuthenticated() )
{
$inactiveUserChannels = $this->userRepository->getUserInactiveChannels( 1 );
}
// Picks
$picks = $this->articleRepository->getArticles( 'picks', 25 );
$ads = $this->sponsorRepository->getWhereNotInCollection( $sponsors, 30 );
$response = $this->patternMaker->make( [ 'articles' => $picks, 'sponsors' => $ads ] );
$picks = $response->articles;
$ads = $response->sponsors;
// Whats on
$channel = 50;
$whatsOn = $this->articleRepository->getArticlesWithEvents(null); // get 20 articles from the whats on channel
$response = $this->patternMaker->make( [ 'articles' => $whatsOn, 'sponsors' => $ads ], "whats-on" );
$whatsOn = $response->articles;
$ads = $response->sponsors;
$channel = $this->channelTransformer->transform( getChannel($channels, $channel) );
$channel['articles'] = $whatsOn;
$channelFeed[] = $channel;
// create a new instance of the channel feed class and pass the required params to it
$this->channelFeed = $this->createChannelFeed( $allChannels, [ 48, 49, 51, 52 ], $ads, $inactiveUserChannels );
if( ! $response = cached("homepage") )
{
$data = [
'channels' => $this->channelTransformer->transformCollection( $channels )
,'adverts' => $this->sponsorTransformer->transformCollection( $sponsors->toArray() )
,'features' => $this->articleTransformer->transformCollection( $this->articleRepository->getArticles( 'featured', 25 ), [ 'showBody' => false] )
,'picks' => $picks
,'channelFeed' => $this->channelFeed->make()
];
cacheIt("homepage", $response, "1 hour");
}
return $this->respondFound(Lang::get('api.homepageFound'), $data);
}
This is early stage and I am refactoring as I go which is where this question of class dependencies has come from.
First, unless your constructor on the MainController have more logic you can just leave out the constructor all together and the BaseController constructor will be called directly.
You could do this:
Class BaseController extends Controller {
protected $classFile;
//repeat for all classes
function classFile() {
if(!$this->classFile) $this->clasFile = new classFile;
return $this->classFile;
}
//repeat for all classes
function useExample() {
$this->classFile()->randomMethod();
}
}
... this way atleast you dont instanciate more than needed.
That beeing said, it sounds like what you really want is Facades, and you could just go:
$channels = ChannelRepository::getChannels();
http://laravel.com/docs/facades
Related
Since few days, I'm trying to learn Symfony Unit Test.
I made my first test but I noticed one thing :
For my first test, I tested a simple implode for some tags :
class TagsTransformerTest extends TestCase
{
public function testTransform()
{
$tagsArray = [
$this->createTag('Symfony'),
$this->createTag('Test'),
$this->createTag('Unit'),
];
$transformer = $this->getMockedTransformer();
$tagsTransformed = $transformer->transform($tagsArray);
$this->assertEquals('Symfony, Test, Unit', $tagsTransformed);
}
private function getMockedTransformer()
{
$entityManager = $this
->getMockBuilder(ObjectManager::class)
->disableOriginalConstructor()
->getMock();
return new TagsTransformer($entityManager);
}
private function createTag($name)
{
$tag = new Tag();
$tag->setName($name);
return $tag;
}
}
As you can see, I have to create a createTag() method to build some tags, but I wonder if I can like Laravel :
$anakin = factory(User::class)->states('anakin')->create();
$post = factory(Post::class)->create(['author_id' => $anakin->id]);
factory(Post::class, 2)->create();
factory(Comment::class, 3)->create(['post_id' => $post->id]);
My question is, is there any way to use a object like factory in Symfony to avoid going through an alternative method ?
Better:
$tagsTransformed = ($this->getMockedTransformer())->transform($tagsArray);
$this->assertEquals('Symfony, Test, Unit', $tagsTransformed);
Yes i think you can, check this
$form = $this->factory->create(TestedType::class);
I needed to select a controller in CakePHP 2.4 and display all the functions written in it. I found how to list controllers from this question & answer thread on Stack Overflow but what I need now is given a specific controller I need to get the list of all functions it contains.
Here what i have done
public function getControllerList() {
$controllerClasses = App::objects('controller');
pr($controllerClasses);
foreach($controllerClasses as $controller) {
$actions = get_class_methods($controller);
echo '<br/>';echo '<br/>';
pr($actions);
}
}
pr($controllerClasses); gives me list of controllers as follows
Array
(
[0] => AppController
[1] => BoardsController
[2] => TeamsController
[3] => TypesController
[4] => UsersController
)
however pr($actions); nothing... :(
here you go the final working snippet the way i needed
http://www.cleverweb.nl/cakephp/list-all-controllers-in-cakephp-2/
public function getControllerList() {
$controllerClasses = App::objects('controller');
foreach ($controllerClasses as $controller) {
if ($controller != 'AppController') {
// Load the controller
App::import('Controller', str_replace('Controller', '', $controller));
// Load its methods / actions
$actionMethods = get_class_methods($controller);
foreach ($actionMethods as $key => $method) {
if ($method{0} == '_') {
unset($actionMethods[$key]);
}
}
// Load the ApplicationController (if there is one)
App::import('Controller', 'AppController');
$parentActions = get_class_methods('AppController');
$controllers[$controller] = array_diff($actionMethods, $parentActions);
}
}
return $controllers;
}
Something like this should do the trick:
https://github.com/dereuromark/cakephp-sandbox/blob/master/Plugin/Sandbox/Controller/SandboxAppController.php#L12
It basically uses a very basic PHP function:
$actions = get_class_methods($Controller);
Then get parent methods:
$parentMethods = get_class_methods(get_parent_class($Controller));
Finally, using array_diff you get the actual actions in that controller:
$actions = array_diff($actions, $parentMethods);
Then you can still filter out unwanted actions.
I am learning and trying to understand things concerning oop so my question might sound very silly.
I want to be able to get to an object directly like this:
$some_urls = new some_urls;
print_r($some_urls->url()->illegal);
Do I have to add the (object) before each array() inside the function or is there a more efficient way ? Thanks and sorry if it is repeated question, I am looking a long time about this.
class some_urls
{
public function url()
{
return (object)array(
'illegal' => (object)array(
'a_path',
'another_path'
),
'legal' => (object)array(
'a_path',
'another_path'
)
);
}
}
EDIT:
I think I found a better alternative based on some ideas by guys here.
You think this is better ?
$some_urls = new some_urls;
print_r($some_urls->get_url('illegal'));
class some_urls
{
public function get_url($data)
{
$url = $this->url();
return $url[$data];
}
protected function url()
{
return array(
'illegal' => array(
'a_path',
'another_path'
),
'legal' => array(
'a_path',
'another_path'
)
);
}
}
You can make use of the stdClass
$object1 = new stdClass();
$object1->a_path = 'hello';
You have many options.
Option 1. Create a standard class, and then add properties as needed
$obj = new stdclass();
$obj->illegal=new stdclass();
Option 2. Just work with an array.
Option 3. Iterate over your array and create an object.
foreach($url as $key=>$value){//Build your object}
Option 4. Use json_decode
$object = json_decode(json_encode($array), FALSE);
Option 5. Add properties to your class.
public variable illegal
So I've got a plugin which produces information for me based on a user's id.
This happens in the plugin's module 'pitch':
public function executeIndex(sfWebRequest $request)
{
$unique_id = $request->getParameter('unique_id');
$this->user = UserTable::getInstance()->getByToken($unique_id);
$this->forward404Unless($this->user);
$this->iplocation=new IPLocation();
$qualified_offers = new QualifiedOffers();
$this->creatives = $qualified_offers->applicableTo($this->user);
$this->match_type = UserDemoTable::getInstance()->getValue($this->user->id, 'match');
// Put the applicable creatives into the session for later use
$userCreatives = $this->creatives;
$this->getUser()->setAttribute('userCreatives', $userCreatives);
}
And then I try to call that attribute on the subsequent template (In a different module called 'home' with a different action):
public function executePage(sfWebRequest $request)
{
$template = $this->findTemplate($request->getParameter('view'), $this->getUser()->getCulture());
$this->forward404Unless($template);
$this->setTemplate($template);
// Grab the creatives applicable to the user
$userCreatives = $this->getUser()->getAttribute( 'userCreatives' );
}
Unfortunately it doesn't work at all.
If I try this from the action where $creatives is initially generated:
$this->getUser()->setAttribute('userCreatives', $userCreatives);
$foo = $this->getUser()->getAttribute('userCreatives');
// Yee haw
print_r($foo);
I am met with great success. I'm essentially doing this, only from two different controllers. Shouldn't that be irrelevant, given that I've added 'userCreatives' to the user's session?
It sounds like you're trying to store objects as user attributes (i.e., in the session).
From Jobeet Day 13:
You can store objects in the user session, but it is strongly discouraged. This is because the session object is serialized between requests. When the session is deserialized, the class of the stored objects must already be loaded, and that's not always the case. In addition, there can be "stalled" objects if you store Propel or Doctrine objects.
Try storing either array or stdClass representations of your objects and then loading them back into "full" objects once you retrieve them.
Here's an example that I used on another project:
class myUser extends sfGuardSecurityUser
{
...
public function setAttribute( $name, $var )
{
if( $var instanceof Doctrine_Record )
{
$var = array(
'__class' => get_class($var),
'__fields' => $var->toArray(true)
);
}
return parent::setAttribute($name, $var);
}
public function getAttribute( $name, $default )
{
$val = parent::getAttribute($name, $default);
if( is_array($val) and isset($val['__class'], $val['__fields']) )
{
$class = $val['__class'];
$fields = $val['__fields'];
$val = new $class();
$val->fromArray($fields, true)
}
return $val;
}
...
}
I'm currently working on an OO PHP application. I have a class called validation which I would like to use to check all of the data submitted is valid, however I obviously need somewhere to define the rules for each property to be checked. At the moment, I'm using arrays during the construction of a new object. eg:
$this->name = array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter
)
One array for each property.
I would then call a static method from the validation class which would carry out various checks depending on the values defined in each array.
Is there a more efficient way of doing this?
Any advice appreciated.
Thanks.
I know the associative array is used commonly to configure things in PHP (it's called magic container pattern and is considered bad practice, btw), but why don't you create multiple validator classes instead, each of which able to handle one rule? Something like this:
interface IValidator {
public function validate($value);
}
$validators[] = new StringLengthValidator(2, 10);
$validators[] = new NotNollValidator();
$validators[] = new UsernameDoesNotExistValidator();
This has multiple advantages over the implementation using arrays:
You can document them (very important), phpdoc cannot parse comments for array keys.
Your code becomes typo-safe (array('reqiured' => true))
It is fully OO and does not introduce new concepts
It is more readable (although much more verbose)
The implementation of each constraint can be found intuitively (it's not in a 400-line function, but in the proper class)
EDIT: Here is a link to an answer I gave to a different question, but that is mostly applicable to this one as well.
Since using OO it would be cleaner if you used classes for validating properties. E.g.
class StringProperty
{
public $maxLength;
public $minlength;
public $required;
public $value;
function __construct($value,$maxLength,$minLength,$required)
{
$this->value = $value;
$this-> maxLength = $maxLength;
$this-> minLength = $minLength;
$this-> required = $required;
}
function isValidat()
{
// Check if it is valid
}
function getValidationErrorMessage()
{
}
}
$this->name = new StringProperty($namefromparameter,10,2,true);
if(!$this->name->isValid())
{
$validationMessage = $this->name-getValidationErrorMessage();
}
Using a class has the advantage of encapsulating logic inside of it that the array (basically a structure) does not have.
Maybe get inspired by Zend-Framework Validation.
So define a master:
class BaseValidator {
protected $msgs = array();
protected $params = array();
abstract function isValid($value);
public function __CONSTRUCT($_params) {
$this->params = $_params;
}
public function getMessages() {
// returns errors-messages
return $this->msgs;
}
}
And then build your custom validators:
class EmailValidator extends BaseValidator {
public function isValid($val=null) {
// if no value set use the params['value']
if ($val==null) {
$val = $this->params['value'];
}
// validate the value
if (strlen($val) < $this->params['maxlength']) {
$this->msgs[] = 'Length too short';
}
return count($this->msgs) > 0 ? false : true;
}
}
Finally your inital array could become something like:
$this->name = new EmailValidator(
array(
'maxlength' => 10,
'minlength' => 2,
'required' => true,
'value' => $namefromparameter,
),
),
);
validation could then be done like this:
if ($this->name->isValid()) {
echo 'everything fine';
} else {
echo 'Error: '.implode('<br/>', $this->name->getMessages());
}