How to stop passing null to `__construct` - php

I have a Service which is called inside a Controller in a Laravel application. Usually these services are implemented using a composition pattern, where the service is injected in the __construct of the controller; then, whenever it's necessary to use the service, you just call the service and the method you want. If you want to create a new register on the database, you just need to call the service, the method, and pass the necessary parameters to that method.
But let's say I don't want to pass parameters. I want an immutable, zero-parameter object. I have tried to implement this in the code bellow (first there's the service and then the controller where the service is called). Notice I have all the parameters I'd need to pass to each method in the constructor of my service. This produces what I want: an immutable, zero-parameter object.
But there's a problem: it's not in every call of the service that I need all the parameters in the constructor (not every method used needs them), that's why they're typed as a [any type]|null. This seems wrong, because if I don't need all the parameters to construct my object, maybe it should be another object altogether. I don't have a problem with small classes (I like it, actually), but it seems just too much to have a CreateCmsUserService, UpdateCmsUserService, DeleteCmsUserService.
I'd like to know if there's an elegant solution for this constructor with null or if the only way to go is dividing the class.
<?php
namespace App\Services;
use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Hash;
use App\Interfaces\CRUD;
class CmsUsersService implements CRUD
{
private ?int $user_id;
private ?array $data;
private ?string $users_to_be_deleted;
public function __construct(?array $data, ?int $user_id, ?string $users_to_be_deleted)
{
$this->user_id = $user_id;
$this->data = $data;
$this->users_to_be_deleted = $users_to_be_deleted;
}
public function create()
{
$this->data['token'] = Hash::make($this->data['email']);
$this->data['password'] = Hash::make($this->data['password']);
User::create($this->data);
return cms_response(trans('cms.users.success_create'));
}
public function update()
{
try {
if (array_key_exists('password', $this->data)) {
$this->data['password'] = Hash::make($this->data['password']);
}
$user = $this->__findOrFail();
$user->update($this->data);
return cms_response(trans('cms.users.success_update'));
} catch (\Throwable $th) {
return cms_response($th->getMessage(), false, 400);
}
}
public function delete()
{
User::whereIn('id', json_decode($this->users_to_be_deleted))->delete();
return cms_response(trans('cms.users.success_delete'));
}
private function __findOrFail()
{
$user = User::find($this->user_id);
if ($user instanceof User) {
return $user;
}
throw new Exception(trans('cms.users.error_user_not_found'));
}
}
<?php
namespace App\Http\Controllers\Cms;
use App\Http\Controllers\Controller;
use App\Http\Requests\UserRequest;
use App\Services\CmsUsersService;
use Illuminate\Http\Request;
class UsersController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(UserRequest $request)
{
$users_service = new CmsUsersService($request->all(), null, null);
$result = $users_service->create();
return redirect()->back()->with('message', $result);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(UserRequest $request, $id)
{
$users_service = new CmsUsersService($request->all(), $id, null);
$result = $users_service->update();
return redirect()->back()->with('message', $result);
}
/**
* Remove the specified resource from storage.
*
* #param string $users_id
* #return \Illuminate\Http\Response
*/
public function destroy($users_id)
{
$users_service = new CmsUsersService(null, null, $users_id);
$result = $users_service->delete();
return redirect()->back()->with('message', $result);
}
}

You could give parameters a default value, like so:
public function __construct(?array $data = [], ?int $user_id = null, ?string $users_to_be_deleted = null)
{
$this->user_id = $user_id;
$this->data = $data;
$this->users_to_be_deleted = $users_to_be_deleted;
}
Now you can use this:
$users_service = new CmsUsersService();
See: https://3v4l.org/InkBY

Related

PHPUnit always takes me to login page,. regardless of what I do

So I am having a bit of an interesting problem. I am trying to build a forum for my website. I am trying to create a test that ensures registered users can submit new forum threads. Now, regardless of what I do, the test always ends up at the login paghe, even though I am logged in.
Here is something interesting, however, when I attempt to dd() the thread immediately after creation, Laravel seems to "skip over" the dd command and I end up with the "Thread title not found on the login page" error. I can dd() the $request object and that prints out fine, however, it seems after creating a new Thread model does Laravel skip over the dd($thread) command and I end up getting the login page once again.
I am pulling my hair out. Why is this happening?
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Tests\TestCase;
use App\Thread;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class CreateThreadsTest extends TestCase
{
use DatabaseMigrations;
function test_an_authenticated_user_can_create_new_forum_threads() {
$this->be(factory('App\User')->create());
$thread = factory('App\Thread')->make();
$this->post('forum/threads', $thread->toArray());
// you are missing this line
$this->get($thread->path())->assertSee($thread->title)->assertSee($thread->body);
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Thread;
class ThreadsController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$threads = Thread::latest()->get();
return view('threads.index', compact('threads'));
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$thread = Thread::create([
'user_id' => auth()->id,
'title' => $request->title,
'body' => $request->body
]);
return redirect($thread->path());
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show(Thread $thread)
{
return view('threads.show', compact('thread'));
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Thread extends Model
{
protected $guarded = [];
public function path() {
return "/forum/threads/" . $this->id;
}
public function replies() {
return $this->hasMany(Reply::class);
}
public function creator()
{
return $this->belongsTo(User::class, 'user_id');
}
public function addReply($reply) {
$this->replies()->create($reply);
}
}
When you make a new model using the factory helper, it doesn't have an ID
for example
So $thread->path() will return just "/forum/threads/" which is not what you want
You have to make the request to the location from the response or query the newly created Thread record
public function test_users_can_create_statuses()
{
$this->be(factory('App\User')->create());
$thread = factory('App\Thread')->make();
$response = $this->post('/forum/threads', $thread->toArray());
$this->get($response->headers->get('Location'))
->assertSee($thread->title);
->assertSee($thread->body);
}

Argument 1 passed to __construct() must be an instance of GuzzleHttp\Client

I'm trying to "use" a vendor script to connect to feefo api (an online reviews service) but when I try and use the script it gives me this error:
Type error: Argument 1 passed to
BlueBayTravel\Feefo\Feefo::__construct() must be an instance of
GuzzleHttp\Client, null given, called in/Users/webuser1/Projects/_websites/domain.co.uk/plugins/gavinfoster/feefo/components/Feedback.php on line 47
Here is the vendor code I'm using:
/*
* This file is part of Feefo.
*
* (c) Blue Bay Travel <developers#bluebaytravel.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace BlueBayTravel\Feefo;
use ArrayAccess;
use Countable;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Support\Arrayable;
use SimpleXMLElement;
/**
* This is the feefo class.
*
* #author James Brooks <james#bluebaytravel.co.uk>
*/
class Feefo implements Arrayable, ArrayAccess, Countable
{
/**
* The guzzle client.
*
* #var \GuzzleHttp\Client
*/
protected $client;
/**
* The config repository.
*
* #var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* The review items.
*
* #var array
*/
protected $data;
/**
* Create a new feefo instance.
*
* #param \GuzzleHttp\Client $client
* #param \Illuminate\Contracts\Config\Repository $config
*
* #return void
*/
public function __construct(Client $client, Repository $config)
{
$this->client = $client;
$this->config = $config;
}
/**
* Fetch feedback.
*
* #param array|null $params
*
* #return \BlueBayTravel\Feefo\Feefo
*/
public function fetch($params = null)
{
if ($params === null) {
$params['json'] = true;
$params['mode'] = 'both';
}
$params['logon'] = $this->config->get('feefo.logon');
$params['password'] = $this->config->get('feefo.password');
try {
$body = $this->client->get($this->getRequestUrl($params));
return $this->parse((string) $body->getBody());
} catch (Exception $e) {
throw $e; // Re-throw the exception
}
}
/**
* Parses the response.
*
* #param string $data
*
* #return \Illuminate\Support\Collection
*/
protected function parse($data)
{
$xml = new SimpleXMLElement($data);
foreach ((array) $xml as $items) {
if (isset($items->TOTALRESPONSES)) {
continue;
}
foreach ($items as $item) {
$this->data[] = new FeefoItem((array) $item);
}
}
return $this;
}
/**
* Assigns a value to the specified offset.
*
* #param mixed $offset
* #param mixed $value
*
* #return void
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
/**
* Whether or not an offset exists.
*
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* Unsets an offset.
*
* #param mixed $offset
*
* #return void
*/
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
/**
* Returns the value at specified offset.
*
* #param mixed $offset
*
* #return mixed
*/
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->data[$offset] : null;
}
/**
* Count the number of items in the dataset.
*
* #return int
*/
public function count()
{
return count($this->data);
}
/**
* Get the instance as an array.
*
* #return array
*/
public function toArray()
{
return $this->data;
}
/**
* Returns the Feefo API endpoint.
*
* #param array $params
*
* #return string
*/
protected function getRequestUrl(array $params)
{
$query = http_build_query($params);
return sprintf('%s?%s', $this->config->get('feefo.baseuri'), $query);
}
}
And here is the code I'm using to try and use the fetch() method from the vendor class:
use Cms\Classes\ComponentBase;
use ArrayAccess;
use Countable;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Support\Arrayable;
use SimpleXMLElement;
use BlueBayTravel\Feefo\Feefo;
class Feedback extends ComponentBase
{
public $client;
public $config;
/**
* Container used for display
* #var BlueBayTravel\Feefo
*/
public $feedback;
public function componentDetails()
{
return [
'name' => 'Feedback Component',
'description' => 'Adds Feefo feedback to the website'
];
}
public function defineProperties()
{
return [];
}
public function onRun()
{
$this->feedback = $this->page['feedback'] = $this->loadFeedback($this->client, $this->config);
}
public function loadFeedback($client, $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
}
Won't allow me to call the Fetch() method statically so trying to instantiate and then use:
public function loadFeedback($client, $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
I've also tried type hinting the args like this:
public function loadFeedback(Client $client, Repository $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
But still I get the exception error above. I'm struggling to understand how to get past this. Any help for a newbie much appreciated :)
Just type hinting the function won't cast it to that object type. You need to properly pass the Guzzle\Client object to your function call.
// Make sure you 'use' the GuzzleClient on top of the class
// or use the Fully Qualified Class Name of the Client
$client = new Client();
$feedback = new Feedback();
// Now we passed the Client object to the function of the feedback class
// which will lead to the constructor of the Feefo class which is
// where your error is coming from.
$loadedFeedback = $feedback->loadFeedback($client);
Don't forget to do the same for the Repository $config from Laravel/Lumen

laravel route and controller

i am a new laravel user and a have admin page which doing update delete and insert my problem is i dont know how to call this functions in route.
Note: all this options working on one page (admin).
so please can anyone help me ?!
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use DB;
class BlogPostController extends Controller
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index(){
$date = date('Y-m-d');
$time = time('H:i:s');
/*$sections = ['History' => 'history.png','Electronics' => 'electronics.png','Electrical' => 'electrical.png','Science' => 'science.png',
'Art'=>'ARt.png','Database'=>'database.png','Irrigation'=>'irrigation.png','Novel'=>'Novel.png','Style'=>'Stsyle.png'];
*/
$sections = DB ::table('sections')->get();
return view('libraryViewsContainer.library')->withSections($sections)->withDate($date)->withTime($time);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create(){
//return View::make('posts.create');
return '<center><h1>Creating new section in the library!</h1></center>';
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store( Request $request){
$section_name = $request->input('section_name');
$file = $request->file('image');
$destinationPath = 'images';
$filename = $file->getClientOriginalName();
$file->move($destinationPath,$filename);
DB ::table('sections')->insert(['section_name'=>$section_name,'image_name'=>$filename]);
return redirect('admin');
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id){
// $post = Post::find($id);
// return View::make('posts.show')->with('post', $post);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id){
// $post = Post::find($id);
// return View::make('posts.edit')->with('post', $post);
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id,Request $request){
$section_name = $request->input('section_name');
DB ::table('sections')->where('id',$id)->update(['section_name'=>$section_name]);
return redirect('admin');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id){
DB :: table('sections')->where('id',$id)->delete();
return redirect('admin');
}
public function admin()
{
$sections = DB ::table('sections')->get();
return view('libraryViewsContainer.admin',['sections'=>$sections]);
}
}
Not entirely sure of the question, but you list the routes in routes.php, under the web group (so it applies default checks).
When you have resources, they'll use CRUD operations (create, read, update, delete) and will correspond to the default operates in the class. Else, make your own function names, and put seperate routes.
Route::group(['middleware' => ['web', 'auth']], function () {
Route::resource('/user', 'UserController');
}
Another option is calling the method direct in your routes:
Route::get('/auth/login', '\App\Http\Controllers\Auth\AuthController#getLogin');
Route::any('/datafeed/{id}/validate', 'DataFeedController#validateQuery');
You'll notice {id} which is the variable available in the function you've selected i.e. function validateQuery($id);
Here's a full example:
class UserController extends BaseController
{
public function __construct(User $model)
{
parent::__construct($model);
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$collection = $this->model->all();
return view('user.index')->with('collection', $collection);
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('user.create');
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$input = Input::except(['_method', '_token']);
$connector = $this->model->create($input);
return redirect()->action('UserController#show', [$connector->id]);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$connector = $this->model->findOrFail($id);
return view('user.show')->with('connector', $connector);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
$connector = $this->model->findOrFail($id);
return view('user.edit')->with('connector', $connector);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$input = Input::except(['_method', '_token']);
$connector = $this->model->findOrFail($id);
$connector->update($input);
return redirect()->action('UserController#show', [$id]);
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
$currentID = Auth::user();
$user = $this->model->findOrFail($id);
if ($currentID->id != $user->id) {
$user->delete();
} else {
Session::flash('flash_message', "You cant delete your own account!");
Session::flash('flash_type', 'alert-danger');
}
return redirect()->action('UserController#index');
}
And another example with a custom route:
Route::any('/datafeed/{id}/execute', 'DataFeedController#execute');
public function execute($id, $test = false) {
$results = $this->executeQuery($id, $test);
return view('datafeed.execute')->with('results', $results);
}
I'm not entirely sure on your plans (or even if you've fully read the documentation?) but you can access these functions by doing something similar to the following in your routes.php or Routes\web.php (depending on your version) file:
Route::get('/blog/create', 'BlogPostController#create');
Route::get('/blog/article/{id}', 'BlogPostController#show');
The first part is defining the route and the second part is saying what method should be run on the defined controller when this route is matched in the address bar. It doesn't always have to be get requests though, you can do get, post, patch and put

Laravel 5: Can't find a class in a controller, 'use App/Something' is stated in the the controller

I would appreciate some help identifying what is wrong I've been staring at this for hours.
local.ERROR: exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'Class 'App\Http\Controllers\App\Type' not found' in C:\wamp\www\Website\app\Http\Controllers\TripController.php:35.
The controller is as follows:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Trip;
use App\Type;
use DB;
class TripController extends Controller
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function newTrip(Request $request)
{
//The sent info
$placeTypes = $request -> input('placeTypeString');
$user = $request -> input('id');
$placeTypesArrays = $request -> input('searchTypesCollectionArray');
//upload trip
Trip::create(['user_id' => $user, 'trip_type' => $placeTypes]);
//associate Trip with its types
$lastInsertedTripId = DB::table('trips')->orderBy('created_at', 'desc')->first();
$types = App\Type::all();
foreach ($placeTypesArrays as $arrayEntry)
{
foreach ($types as $type)
{
$typeName = $type->name;
if($typeName==$arrayEntry)
{
$Types->propertyOf()->attach($trip_id);
}
}
}
//upload
Trip::create(['user_id' => $user, 'trip_type' => $placeTypes]);
}
// //returns all posts in an app friendly manner
// public function indexJson()
// {
// //cache the search
// $result= Cache::remember("reply",1,function()
// {
// $posts = Post::orderBy('created_at', 'desc')->get()->first();
// return $posts;
// });
// return $result;
// }
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param Request $request
* #return Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param Request $request
* #param int $id
* #return Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
//
}
}
The Model is written as follows:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Trip;
class Type extends Model
{
//
public function propertyOf()
{
return $this->belongsToMany('App\Trip');
}
}
You need to either use \App\Type or Type. The first one is the fully qualified class name, the second is the class you imported with your use statement. When you do App\Type, which is neither a class name or fully qualified class name, PHP is looking for the class in current namespace.
Replace
$types = App\Type::all();
with
$types = Type::all();

method does not exist on this mock object - Laravel , Mockery

i'm trying to test a simple class. I'm following this tutorial( http://code.tutsplus.com/tutorials/testing-laravel-controllers--net-31456 ).
I have this error, while running tests:
Method Mockery_0_App_Interfaces_MealTypeRepositoryInterface::getValidator() does not exist on this mock object
Im using repository structure. So, my controller calls repository and that returns Eloquent's response.
I'm relatively new in php and laravel. And I've started learning to test a few days ago, so I'm sorry for that messy code.
My test case:
class MealTypeControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->mock = Mockery::mock('App\Interfaces\MealTypeRepositoryInterface');
$this->app->instance('App\Interfaces\MealTypeRepositoryInterface' , $this->mock);
}
public function tearDown()
{
Mockery::close();
}
public function testIndex()
{
$this->mock
->shouldReceive('all')
->once()
->andReturn(['mealTypes' => (object)['id' => 1 , 'name' => 'jidlo']]);
$this->call('GET' , 'mealType');
$this->assertViewHas('mealTypes');
}
public function testStoreFails()
{
$input = ['name' => 'x'];
$this->mock
->shouldReceive('getValidator')
->once()
->andReturn(Mockery::mock(['fails' => true]));
$this->mock
->shouldReceive('create')
->once()
->with($input);
$this->call('POST' , 'mealType' , $input ); // this line throws the error
$this->assertRedirectedToRoute('mealType.create');//->withErrors();
$this->assertSessionHasErrors('name');
}
}
My EloquentMealTypeRepository:
Nothing really interesting.
class EloquentMealTypeRepository implements MealTypeRepositoryInterface
{
public function all()
{
return MealType::all();
}
public function find($id)
{
return MealType::find($id);
}
public function create($input)
{
return MealType::create($input);
}
public function getValidator($input)
{
return MealType::getValidator($input);
}
}
My eloquent implementation:
Nothing really interresting,too.
class MealType extends Model
{
private $validator;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'meal_types';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [];
public function meals()
{
return $this->hasMany('Meal');
}
public static function getValidator($fields)
{
return Validator::make($fields, ['name' => 'required|min:3'] );
}
}
My MealTypeRepositoryInterface:
interface MealTypeRepositoryInterface
{
public function all();
public function find($id);
public function create($input);
public function getValidator($input);
}
And finally, My controller:
class MealTypeController extends Controller {
protected $mealType;
public function __construct(MealType $mealType)
{
$this->mealType = $mealType;
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
$mealTypes = $this->mealType->all();
return View::make('mealTypes.index')->with('mealTypes' ,$mealTypes);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$mealType = new MealTypeEloquent;
$action = 'MealTypeController#store';
$method = 'POST';
return View::make('mealTypes.create_edit', compact('mealType' , 'action' , 'method') );
}
/**
* Validator does not work properly in tests.
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$input = ['name' => $request->input('name')];
$mealType = new $this->mealType;
$v = $mealType->getValidator($input);
if( $v->passes() )
{
$this->mealType->create($input);
return Redirect::to('mealType');
}
else
{
$this->errors = $v;
return Redirect::to('mealType/create')->withErrors($v);
}
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
return View::make('mealTypes.show' , ['mealType' => $this->mealType->find($id)]);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
$mealType = $this->mealType->find($id);
$action = 'MealTypeController#update';
$method = 'PATCH';
return View::make('mealTypes.create_edit')->with(compact('mealType' , 'action' , 'method'));
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
$mealType = $this->mealType->find($id);
$mealType->name = \Input::get('name');
$mealType->save();
return redirect('mealType');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
$this->mealType->find($id)->delete();
return redirect('mealType');
}
}
That should be everything. It's worth to say that the application works, just tests are screwed up.
Does anybody know, why is that happening? I cant see a difference between methods of TestCase - testIndex and testStoreFails, why method "all" is found and "getValidator" is not.
I will be thankful for any tips of advices.
Perhaps an aside, but directly relevant to anyone finding this question by its title:
If:
You are getting the error BadMethodCallException: Method Mockery_0_MyClass::myMethod() does not exist on this mock object, and
none of your mocks are picking up any of your subject's methods, and
your classes are being autoloaded, (e.g. using composer)
then before making your mock object, you need to force the loading of that subject, by using this line of code:
spl_autoload_call('MyNamespace\MyClass');
Then you can mock it:
$mock = \Mockery::mock('MyNamespace\MyClass');
In my PHPUnit tests, I often put that first line into the setUpBeforeClass() static function, so it only gets called once and is isolated from tests being added/deleted. So the Test class looks like this:
class MyClassTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
spl_autoload_call('Jodes\MyClass');
}
public function testIt(){
$mock = \Mockery::mock('Jodes\MyClass');
}
}
I have forgotten about this three times now, each time spending an hour or two wondering what on earth the problem was!
I have found a source of this bug in controller.
calling wrong
$v = $mealType->getValidator($input);
instead of right
$v = $this->mealType->getValidator($input);

Categories