I spent a little while trying to figure out how to implement a sub menu bar and eventually decided all I really want is a little helper method that appends my template to the current view instead of an actual view helper:
//To use in any action requiring the sub navbar to be displayed
protected function addSubNav(ViewModel $view) {
$subNavView = new ViewModel();
$subNavView->setTemplate('helpdesk/helpdesk/subNav');
$view->addChild($subNavView, 'subNav');
return $view;
}
But when I call it in a method like this $this->subNav in my template is null:
public function indexAction() {
//return new ViewModel();
$this->addSubNav(new ViewModel());
}
When doing $this->subNav in index.phtml is NULL, why is that?
addSubNav() should be returning the view which I appended a template to.
You don't return your view model a the end of your action
public function indexAction() {
return $this->addSubNav(new ViewModel());
}
Related
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 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 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');
I am pulling my hair out with this one. I have looked and cannot find a simple, clear example of creating and using a partial stub with Microsoft Moles. Maybe I'm missing somethimg, or have my code architected poorly, but I can't seem to get this to work.
Here's my class (simplified):
public class AccountService : IAccountService {
private readonly webServiceProxy IExternalWebServiceProxy;
public AccountService(IExternalWebServiceProxy webServiceProxy) {
this.webServiceProxy = webServiceProxy;
}
public List<AccountModel> GetAccounts(string customerId) {
var returnList = new List<AccountModel>();
var xmlResponse = webServiceProxy.GetAllCustomerAccounts(customerId);
var accountNodes = xmlResponse.SelectNodes("//AccountNodes");
if (accountNodes != null)
{
foreach (XmlNode node in accountNodes)
{
var account = this.MapAccountFromXml(node);
if (!string.IsNullOrEmpty(account.AccountNumber))
{
returnList.Add(account);
}
}
}
return returnList;
}
public AccountModel MapAccountFromXml(XmlNode node) {
if (!IsValidAccount(node) {
return null;
}
// This performs a lot of XML manipulation getting nodes based on attributes
// and mapping them to the various properties of the AccountModel. It's messy
// and I didn't want it inline with the other code.
return populatedAccountModel;
{
public bool IsValidAccount(XmlNode node)
{
var taxSelectValue = node.SelectSingleNode("//FORMAT/Field[#taxSelect='1']").First().Value;
var accountStatus = // similar to first line in that it gets a single node using a specific XPath
var maturityDate = // similar to first line in that it gets a single node using a specific XPath
var maturityValue = // similar to first line in that it gets a single node using a specific XPath
return taxSelectValue != string.Empty && taxSelectValue != "0" && (accountStatusValue != "CL" || (maturityDate.Year >= DateTime.Now.AddYears(-1).Year));
}
}
What I want to do is test my GetAccounts() method. I can stub out the IExternalWebServiceProxy call and return fake XML, but I have internal calls happening in my service since my GetAccounts() method calls MapAccountFromXml() which in turn calls IsValidAccount().
Perhaps the solution is to not worry about breaking out the long and involved MapAccountFromXml() and IsValidAccount() code and just put them inline into the GetAccount() call, but I would rather leave them broken out for code readability.
I have my Moles assembly created, and know I can create a stub version of my class like this
var stubWebService = SIExternalWebServiceProxy {
GetAllCustomerAccounts = delegate {
return SomeHelper.GetFakeXmlDocument();
}
}
var stubAccountService = new SAccountService() { callsBase = true; }
My problem is I don't know how to then override the internal calls to MapAccountFromXml and IsValidAccount and I don't want my Unit Test to be testing thos methods, I'd like to isolate GetAccounts for the test. I read somewhere the methods need to be virtual to be overriden in a partial stub, but could not find anything that then showed how to create a stub that overrides a few methods while calling the base for the one I want to test.
Peer put me on the right track, thank you.
It turned out that what I was looking for is called Detours in Moles. Rather than stub an interface using
var stubAccountService = new SIAccountService();
what I needed to do was create an instance of my AccountService and then detour all calls to the methods I wanted to mock, like this
var accountService = new AccountService();
MAccountService.AllInstances.MapAccountFromXmlXmlNode = delegate {
return new AccountModel();
};
The MAccountService is provided by Moles when you Mole your assembly. The only missing piece to this is that for this to work you need to add the following attribute to your test method:
[HostType("Moles")]
This worked for me locally, but in the end I had trouble getting TFS to do automated builds
UPDATE
I just stumbled on another way of doing this, while looking at Rhino Mocks. If the methods in the class being mocked are virtual then you can override them in the mock, like this:
var accountService = new SAccountService();
accountService.MapAccountFromXmlXmlNode = delegate
{
return new AccountModel();
}
Now I can call
accountService.GetMemberAccounts();
and when accountService makes its call to MapAccountFromXml it will be caught by the stub and processed as I deem necessary. No messing with HostType and it works like a charm.
To test methods in you class in issolation you do this with moles by making a mole for the IsValidAccount and MapAccountFromXml methods. Or make a stub implementation with stubs where you let the stub call the orriginal methode using base. Or what I think is a nicer solution, make a test class which overrides the methods you do want to stub (this is the same what a stub would do, except you see all what is happening in your own code):
public class TestHelperAccountService : AccountService {
public override AccountModel MapAccountFromXml(XmlNode node) {
return new AccountModel(){
//Accountmodelstub
};
{
public override bool IsValidAccount(XmlNode node)
{
return true;
}
}
This way you can do your test for the GetAccount method on your TestHelperAccountService class where you GetAccount method runs in full issolation. You can do the same for the methods like MapAccountFromXml to test them seperatly.
I have a controller (Controller_Product) that extends Controller_Template.
In the Controller_Product I have some actions (create, edit, etc.) where I need the template to be rendered, but some actions (ex. save, delete) have to return a json object, so I don't need the template to be rendered.
How can I solve this problem?
I can set the $this->auto_render to FALSE in my save or delete action, but the template will be created in this case too, even if will be no rendered. I think this is not very elegant to load a template when I don't actually need it.
Any suggestions?
Something along these lines perhaps:
public function before()
{
if (in_array($this->request->action(), array('save', 'delete')))
{
$this->auto_render = FALSE;
}
parent::before();
}
[edit]
A better approach might be to check for an ajax request:
public function before()
{
if ($this->request->is_ajax())
{
$this->auto_render = FALSE;
}
parent::before();
}