In my Laravel 4 project I've bound the current user to the views using the share() method like so:
View::share(['currentUser' => Sentry::getUser()]);
This works when browsing the site, all the views have access to the variable $currentUser. However, when attempting to test my application, the variable is never bound, despite a user definitely being logged in.
class PagesControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
// This works, as halting the application and dumping the user manually demonstrate it as such.
Sentry::login(User::first());
}
public function testIndex()
{
$this->get('/');
$this->assertResponseOk();
}
}
However, this simply results in a stack-trace of errors...
ErrorException: Trying to get property of non-object (View: ~/Sites/laravel/app/views/layouts/application.blade.php) (View: ~/Sites/laravel/app/views/layouts/application.blade.php)
...
Caused by
ErrorException: Trying to get property of non-object (View: ~/Sites/laravel/app/views/layouts/application.blade.php)
...
Caused by
ErrorException: Trying to get property of non-object
The exact line this fails at is where the view tries to access the $currentUser variable.
If I use a view composer instead, like follows, it solves the problem in this instance - but I want the variable available in ALL views, not just the ones I specify, and I'd also like to know WHY this is occurring.
View::composer('layouts.application', function($view)
{
$view->with('currentUser', Sentry::currentUser());
});
I'm guessing you are doing View::share in app/start/global.php. This file is invoked by calling parent::setUp(), which is before you've done Sentry::login, and thus, $currentUser will be null. You should either find a way to delay the View::share (using a view composer is one way to do this) or just use Sentry::getUser() in your views.
Related
I am trying to implement a simple roll-a-dice service and use it in a .phtml file. I know my issue has been reported often on SO, but I could not find a solution in other questions.
I get the following error message:
Fatal error: Call to a member function getDiceResult()
on a non-object in rolladice.phtml on line 11
Here is line 11:
<?php
$result = $this->rollADiceService->getDiceResult();
echo "<p>Roll-a-dice result: ".$result."</p>";
?>
The controller is set-up as following:
class RollADiceController extends AbstractActionController
{
private $rollADiceService;
public function setPluginManager(PluginManager $plugins) {
parent::setPluginManager($plugins);
$this->rollADiceService = $this->getServiceLocator()
->get('RollADiceService');
}
...
In Module.php, I have:
public function getServiceConfig()
{
return array(
'factories'=>array(
'LoginLogoutService' => function() {
return new LoginLogoutService();
},
'RollADiceService' => function() {
return new RollADiceService();
},
),
);
}
I am using the same technique (i.e., setPluginManager) to retrieve instances of my services in other controllers without issues. What am I doing wrong?
P.S.: Using a debugger, I can see that setPluginManager() is called, but that the $this->rollADiceService variable is initialized with null. Why?
Ultimately, there were two issues:
i) I refactored my code to pass the result of the call to rollADiceService->getDiceResult() as a variable which $this can access.
ii) Hijacking setPluginManager() is not a recommended practice. I've implemented factories for my controllers depending on services.
your problem is realy simple I think.
<?php
$result = $this->rollADiceService()->getDiceResult();
echo "<p>Roll-a-dice result: ".$result."</p>";
?>
Add these brackets after rollADiceService should fix your problem. Without brackets, Zend View instance try to access to view variable named rollADiceService, which is obviously NULL.
By adding brackets, you tell to View it should look for rollADiceService in registered view helpers.
I hope it helps :)
I've been debugging line per line and it seems the error comes when I try to bind a repository to a mock:
$mock = Mockery::mock('MyNamespace\Repositories\MyModelInterfaceRepository');
// Error is in this line
$this->app->instance('MyNamespace\Repositories\MyModelInterfaceRepository', $mock);
I already bound repo interface and implementation and it works on the browser, it only fails in the test case, giving me an error 500.
My controller's constructor goes like this:
use MyNamespace\Repositories\MyModelInterfaceRepository;
class MyController extends Controller {
public function __construct(MyModelInterfaceRepository $repo) {
$this->repo = $repo;
}
....
Any ideas?
Edit:
Here is the log. It seems to be something about the View not receiving a proper foreach argument, which is possibly caused because the mock call is returning null.
For anyone who also encounters this problem, in this case, should the controller validate if the returned value is null (considering Eloquent would return an empty array if no records, but never null), or should the Mockery make sure it returns a value?
I'm new to Laravel and the concept of the IoC. I was following the great tutorials over a Nettuts (http://net.tutsplus.com/tutorials/php/testing-laravel-controllers/) and was able to successful test my controller. However I wanted to isolate the controller by mocking the database. As soon as I attempted to inject my mocked object into the IoC I get the following error:
Cannot modify header information - headers already sent by (output started at /Users/STRATTON/Dev/SafeHaven/vendor/phpunit/phpunit/PHPUnit/Util/Printer.php:172)
The line it's referring to outputs PHPUnit's buffer using the 'print' construct. Something is causing output to be sent before headers are set but I can't track down the problem.
I'm able to run all my tests successfully when the controller calls the real model and makes the database call. At the same time I'm able to mock the object successfully and exercise mock without error. But as soon as I attempt to inject the mocked object using App::instance() the error appears.
I've also tested this with PHPUnit's mocks and get the same results. Am I mocking the object properly? Do I have a problem with namespacing? Am I missing something that's outputting content?
Controller:
<?php namespace App\Controllers;
use App\Models\Repositories\ArticleRepositoryInterface;
class HomeController extends BaseController {
protected $articles;
public function __construct(ArticleRepositoryInterface $articles)
{
$this->articles = $articles;
}
public function index()
{
$articles = $this->articles->recent();
return \View::make('home.index')
->with('articles', $articles);
}
}
TestCase
<?php namespace Tests\Controllers;
class HomeControllerTest extends \TestCase {
public function testIndex()
{
$mocked = \Mockery::mock('App\\Models\\Repositories\\ArticleRepositoryInterface');
$mocked->shouldReceive('recent')->once()->andReturn('foo');
\App::instance('App\\Models\\Repositories\\ArticleRepositoryInterface', $mocked);
//$mocked->recent();
$this->call('GET', '/');
$this->assertResponseOk();
$this->assertViewHas('articles');
}
}
It actually a a bug on how exception is handled during running test case, this however has been fixed, just run composer update.
Answering my own question - The reason for the error is that some part of the code is causing a PHP error or an exception to be thrown.
In this case, the problem was an Exception thrown from the View. The View was expecting that the value returned by the method recent() was an Eloquent Collection (Illuminate\Database\Eloquent\Collection), or at least something that the View could iterate over.
THe HomeControllerTest::TestIndex method was mocking the object and when recent() was called it returned 'foo'. There's no way for the View to iterate over a string so it throws an exception. The two solutions are below, the later allowing the ability to test that the view received the correct object type.
$mocked->shouldReceive('recent')
->once()
->andReturn([]);
If you're having a similar issue, examine all the code being tested and make sure you're tests actually fulfill all the requirements/dependancies... or use TDD... something I should have done from the start and avoided this issue.
I have the following code but it does not seem to want to work chained.
$this->view->setData($class_vars);
$this->view->render('addview');
The above works and runs fine but when i try to do the following:
$this->view->setData($class_vars)->render('addview');
I get the following error:
Fatal error: Call to a member function render() on a non-object in....
But the strange thing is when i call it the other way:
$this->view->render('addview')->setData($class_vars);
It runs, but I need the setData to run first as this sets up the var for the actual view, so even though i get the view its got errors where the vars should be? Both methods are public?
Thanks You
Does setData() return the view object (i.e. it has return $this; line)? If not... well it should if you want it to work this way.
For further reference. This technique is called 'fluent interface' and is described here:
http://www.martinfowler.com/bliki/FluentInterface.html
Having a very strange issue with an error on a codeigniter site.
Fatal error: Call to undefined method Document::get_by_module()
The line of code causing this (in a controller) is:
$this->document_type->get_by_module('module1');
The constructor of the controller:
function __construct(){
parent::__construct();
$this->load->model('document','document_type');
}
The document_type class looks like this
class Document_type extends CI_Model {
function Document_type () {
parent::__construct();
}
function get_by_module($prefix) {
// code
}
}
The main issue I'm seeing is that it's saying Document:: is the class, but it should be Document_type. I see no reason that it should be looking in the document class for that function.
If I remove loading of the 'document' class from the controller constructor, the error goes away (but other things break).
Not sure how something like that could be happening.
Looks like you are loading in the wrong model file. The line
$this->load->model('document','document_type');
means something along the lines of: Find me a model named "Document" create an instance and put under $this->document_type. (see the 4th example)
Looks like you have a Document model so the load succeeds, but if you don't want to rename your instance put under the $this (controller instance) you shouldn't use the second parameter in the $this->load->model() line.
Simply write $this->load->model('document_type');