I want to make some action (php script) before all actions in my frontend app and then pass a result from that script to actions in variable - so I can get variable value from all actions. Where should I declare sth like this?
If the filter solution dont feet your needs, you can also create a base action class with a preExecute function:
// app/frontend/lib/baseActions.class.php
class baseActions extends sfActions
{
public function preExecute()
{
$this->myVar = .... // define your vars...
}
}
Then your module actions class extends your baseActions class:
// app/frontend/modules/myModule/actions/actions.class.php
class myModuleActions extends baseActions
{
public function executeIndex(sfWebRequest $request)
{
// var $this->myVar is available in any action and in your template
...
}
}
if you have to use the preExecute function in your module class action, remember to call parent::preExecute() in it.
What kind of information ?
I would recommend you to use filters.
In your apps/frontend/config/filters.yml:
rendering: ~
myfilter:
class: myCustomFilter
Create the file lib/filter/myCustomFilter.php:
<?php
class myCustomFilter extends sfFilter
{
public function execute ($filterChain)
{
if ($this->isFirstCall())
{
// do what ever you want here.
$config = Doctrine_Core::getTable('Config')->findAll();
sfConfig::set('my_config', $config);
}
$filterChain->execute();
}
}
And then, every where, you can retrieve your data:
sfConfig::get('my_config');
Related
I want to add one script inside the head, however using event I could not find way how it works.
I had install one event which added one script but it displays before HTML tag. But i want to display inside head tag.
extention/module/shop.php
class ControllerExtensionModuleShop extends Controller {
public function index() {
//$this->document->addStyle('catalog/view/javascript/css/test.css');
//$this->document->addScript('catalog/view/javascript/js/test.js');
echo "<script src="catalog/view/javascript/js/test.js" type="text/javascript"></script>";
}
}
Admin
class ControllerExtensionModuleShop extends Controller {
public function install() {
$this->model_setting_event->addEvent('shop', 'catalog/view/common/header/before', 'extension/module/shop/index');
}
public function uninstall() {
$this->model_setting_event->deleteEventByCode('shop');
}
Add your event with this trigger:
$this->model_setting_event->addEvent('shop', 'catalog/controller/common/header/before', 'extension/module/shop/index');
And your shop.php should be:
<?php
class ControllerExtensionModuleShop extends Controller {
public function index(&$route = '', &$data = array(), &$output = '') {
$this->document->addScript('catalog/view/javascript/js/test.js');
}
}
I've created myself a helper class called Perm that is meant to return user of current session (I know, I could use default auth/user, but that wouldn't be as much of a fun as creating one from scratch!)
..sadly, the created helper class only works inside a view, but doesn't work at all in controllers.. which kinda misses the point.
Whenever I'm trying to use it inside a controller, it pops:
"Class 'App\Http\Controllers\Perm' not found"
I would most appreciate any help.
HelperServiceProvider.php:
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
public function boot()
{
//
}
public function register()
{
foreach( glob (app_path().'/Helpers/*.php' ) as $filename ) // register all helpers
{
require_once($filename);
}
}
}
Helpers/PermHelper.php:
use App\User;
class Perm
{
public static function user()
{
if(!session('user_id')) return null;
return User::find(session('user_id'));
}
}
Portion of config/app.php, the 'providers' array:
// Custom
App\Providers\HelperServiceProvider::class,
If you are having this issue aswell.
Use proper namespacing.
I have a class which has a method that makes use of PHP's global file_get_contents function. I need to test the method on the class, without actually calling the global function.
I know that I could use namespaces to override what gets returned from file_get_contents, however my tests are in a separate namespace already so I cannot simply match namespaces with the class.
Here's some code:
The class
<?php
namespace MyVendor\MyProject;
class MyClass
{
private $someProperty;
public function __construct($override = '')
{
$this->someProperty = $override;
}
public function myMethod()
{
$request = 'http://example.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
The test
<?php
namespace MyProjectTests;
public function test_it_does_something_with_the_response()
{
$sut = new MyClass();
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
I need to mock the someMethodUsingGlobals() method on the class, but not entirely sure how to go about this.
Solution: do a class-wrapper around the native function
The simplest and cleanest way to do it is to create a wrapper class around the native function.
If you follow DDD and/or hexagonal architecture, you'd probably place it in the "adapters" space, and if you do not follow DDD nor hex-arch, place it besides any group of classes that "touch the exterior world".
This wrapper is a one-liner class:
<?php
declare( strict_types = 1 );
namespace MyVendor\MyProject\Adapters;
class FileGetContentsWrapper
{
public function fileGetContents( string $filename )
{
return file_get_contents( $filename );
}
}
This class cannot be tested, as it just uses the native function.
But with it, you just "shift" the "untesting" to this one-liner class and you now make all the other places that used to use the file_get_contents() testable gaining coverage around teh logic that surrounded the code besides the file reading.
Your original class, modified
You proceed like this:
You inject services via constructor.
You treat the wrapper as a service.
If you are using frameworks like symfony in their recent versions you can use auto-wiring for the injection to simplify the construction of the class.
For example, your class could result in this:
<?php
namespace MyVendor\MyProject;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
class MyClass
{
private $fileGetContentsWrapper;
public function __construct( FileGetContentsWrapper $fileGetContentsWrapper )
{
$this->fileGetContentsWrapper = $fileGetContentsWrapper;
}
/* ... */
public function someMethodUsingTheWrapper( $url )
{
$contents = $this->fileGetContents( $url );
return json_decode( $contents, true )[ 'results' ][ 0 ];
}
}
How to test it
You do the following:
In a test class, you setup a mock for the wrapper.
Within the test method you configure the mock to return whatever you need.
You invoke your class normally.
For example:
<?php
declare( strict_types = 1 );
namespace MyProjectTests;
use MyVendor\MyProject\Adapters\FileGetContentsWrapper;
use MyVendor\MyProject\MyClass;
use PHPUnit\Framework\TestCase;
class MyClassTest extends TestCase
{
private $fileGetContentsWrapper;
//---------------------------------------------------------------------//
// Setup //
//---------------------------------------------------------------------//
protected function setUp()
{
$this->fileGetContentsWrapper = $this->createMock( FileGetContentsWrapper::class )
parent::setUp();
}
//---------------------------------------------------------------------//
// Tests //
//---------------------------------------------------------------------//
public function testSomeMethodUsingTheWrapper()
{
$sut = $this->getSut();
$someSimulatedJson = '{"results":["abc","xyz"]}';
$this->fileGetContentsWrapper->method( 'fileGetContents' )->willReturn( $someSimulatedJson );
$this->assertEquals( 'xyz', $sut->someMethodUsingGlobals( 'dummy-url' ) );
}
//---------------------------------------------------------------------//
// Private //
//---------------------------------------------------------------------//
private function getSut() : MyClass
{
return new MyClass( $this->fileGetContentsWrapper );
}
}
That's all! Hope to help!
You can archive it using a partially Mock Objects: you can mock only a specific method of your class and execute (and tests) the other method.
Further reference here
As example, suppose your modified class:
<?php
namespace Acme\DemoBundle\Service;
class MyClass {
public function myMethod()
{
$request = 'http://domain.com';
$response = $this->someMethodUsingGlobals($request);
// Do something with the response..
return $response;
}
public function someMethodUsingGlobals($url)
{
return json_decode(file_get_contents($url),true)['results'][0];
}
}
You can test with the following test class:
<?php
namespace Acme\DemoBundle\Tests;
class MyProjectTest extends \PHPUnit_Framework_TestCase
{
public function test_it_does_something_with_the_response()
{
$sut = $this->getMock('Acme\DemoBundle\Service\MyClass', array('someMethodUsingGlobals') );
// Set up the expectation for the someMethodUsingGlobals() method
// to be called only once and with the string 'http://domain.com'
// as its parameter.
$sut->expects($this->once())
->method('someMethodUsingGlobals')
->with($this->equalTo('http://domain.com'))
->willReturn('Some expectation');
$response = $sut->myMethod();
$this->assertEquals('Some expectation', $response);
}
}
So the method someMethodUsingGlobals is not executed and return the values defined in the mock deifinition. The method myMethod is executed and processed with the mocked function.
Hope this help
I want to make my controller thin and to separate business-logic from other operations. For example I have an action:
public function indexAction()
{
$languages = $this ->getEntityManager()
->getRepository('\ApanelLanguage\Entity\LanguageCommon')
->getLanguagesList();
$viewModel = new ViewModel(['languages' => $languages]);
return $viewModel;
}
but I want to get action like this:
public function indexAction()
{
$model = $new LanguageModel();
$model->getLanguagesList();
return $viewModel;
}
Is it possible to do? What must I have in Language/Model/LanguageModel ?
Thank you
Removing the business logic from your controller is a great idea for code reuse and maintainability; however I would recommend against moving the logic to your models. A better solution would be to add a service layer to your application.
What is a service layer? Martin Fowler describes it as the following:
[A service layer] defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.
This essentially means that we add a class in-between your controller and your model.
The great advantage of this approach is that should you need to update the business logic of your application there is no need to update the controller. The controller also becomes unaware of any specific code and therefore can be reusable in other unrelated projects.
This 'service' could have a simple API, for example:
interface ServiceInterface
{
public function setObjectManager($objectManager);
public function setRepository($respository);
public function find($id);
public function fetchRow($criteria);
public function fetchAll($criteria);
public function insert($object);
public function update($object);
public function delete($object);
}
Then you can implement this interface for your new 'LanguageService'.
class LanguageService implements ServiceInterface
{
// ... all methods from interface
public function getLanguageList()
{
return $this->repository->getLanguagesList();
}
}
Lastly update your controller to use the new service
class FooController extends AbstractActionController
{
protected $languageService;
public function __construct(ServiceInterface $languageService)
{
$this->languageService = $languageService;
}
public function indexAction()
{
$languages = $this->languageService->getLanguageList();
$viewModel = new ViewModel(['languages' => $languages]);
return $viewModel;
}
public function insertAction()
{
$request = $this->getRequest();
$service = $this->languageService;
$form = $service->getInsertForm();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
// if our form used the DoctrineObjectHydrator
// we will get a entity back populated with the
// form data
$language = $service->insert($form->getData());
if ($language instanceof Entity\Language) {
// success
} else {
// failure
}
}
}
//
}
}
I'm building a Laravel 5 application at the moment and have gotten myself confused about how to mock things in PhpSpec.
I'm building a schedule times validator that requires the intended schedule to be checked against all current schedules and see if there's any overlap (events are not allowed to overlap).
I need to pull in the schedules in question so I can test against them. At the moment it's a very basic whereBetween query, but it's going to get a lot more complicated as there'll be recurring schedules to check against as well.
So here's my stripped down class. I really just want to test the doesNotOverlap function.
use App\Schedule;
class ScheduleTimesValidator
{
protected $schedule;
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->getSchedulesBetween($slug, $intended);
if(empty($schedules)) return true;
return false;
}
protected function getSchedulesBetween($slug, $intended)
{
// Casting to array to make testing a little easier
return $this->schedule->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']])
->get()->toArray();
}
and here's my Spec
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ScheduleTimesValidatorSpec extends ObjectBehavior
{
protected $validIntended = [
'start' => '2015-12-01 12:00:00',
'end' => '2015-12-01 13:00:00'
];
protected $churchNonRecurringSchedules = [
['start' => '2014-11-20 13:00:00', 'end' => '2014-11-21 14:00:00'],
['start' => '2014-11-23 10:36:07', 'end' => '2014-11-23 11:36:07'],
];
function let($schedule)
{
$schedule->beADoubleOf('App\Schedule');
$this->beConstructedWith($schedule);
}
function it_is_initializable()
{
$this->shouldHaveType('App\Validation\ScheduleTimesValidator');
}
function it_should_return_true_if_it_does_not_overlap($schedule)
{
// $schedule->any()->willReturn([]);
// $schedule->whereIsRecurring()->shouldBeCalled();
// $schedule->whereIsRecurring(false)->ofChurch()->whereBetween()->get()->toArray()->willReturn([]);
// $schedule->willReturn([]);
// $this->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
// Tear Down
function letgo() {}
}
If I run it like that I get:
! it should return true if it does not overlap
method 'Double\App\Schedule\P8::whereIsRecurring()' not found.
I tried (as you can see) various commented out things to mock what $schedule will return, but that doesn't seem to work.
So I guess I want to mock the protected getSchedulesBetween method in the class, but doing things like $this->getSchedulesBetween($arg, $arg)->willReturn(blah) doesn't work.
Do I need to pull getSchedulesBetween() out of the class and move it into another class and then mock that? Or do I need to push $this->schedule->blah into the doestNotOverlap method so I can mock what $schedule will return?
I don't want to actually test the App\Schedule Laravel Model - I just want to mock what it's returning and will be hardcoding a variety of queries that will be run to get the different model results.
End of a long day here so brain a little zonked.
Update 2014-10-23
So I created a scope on my Schedule model
public function scopeSchedulesBetween($query, $slug, $intended)
{
return $query->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']]);
}
Then created a new App\Helpers\ScheduleQueryHelper which instantiated App\Schedule as a variable and added this method:
public function getSchedulesBetween($slug, $intended)
{
return $this->schedule->schedulesBetween($slug, $intended)->get()->toArray();
}
Then updated my spec to do
function let($scheduleQueryHelper)
{
$scheduleQueryHelper->beADoubleOf('App\Helpers\ScheduleQueryHelper');
$this->beConstructedWith($scheduleQueryHelper);
}
function it_should_return_true_if_it_does_not_overlap($scheduleQueryHelper)
{
$scheduleQueryHelper->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
And back in my ScheduleTimesValidator class did
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->scheduleQueryHelper->getSchedulesBetween($slug, $intended);
if(empty($schedules)) {
return true;
}
return false;
}
And now PhpSpec is mocking that other class ok. However this seems like a very roundabout way to be doing things.