Laravel partial mock doesn't work with Eloquent model - laravel

I got this error when I try to partially mock an Eloquent model:
Received Mockery_1_App_Models_Invoice::__construct(), but no expectations were specified
I only want to mock the method "getPdf" of my model.
My service
class MyService
{
public function sendPdf($invoiceId)
{
// Invoice::class extend: Illuminate\Database\Eloquent\Model
$invoiceRepository = Container::getInstance()->make(Invoice::class);
$invoice = $invoiceRepository->find($invoiceId);
$invoice->getPdf();
}
}
The test
it('a test', function () {
$filePath = '/path-of-a-pdf.pdf';
$this->partialMock(Invoice::class, function (MockInterface $mock) use ($filePath) {
$mock->shouldReceive('getPdf')->atLeast()->once()->andReturn($filePath);
});
$invoice = Invoice::factory()->create([
'type' => 'invoice'
]);
$myService = new MyService();
$myService->sendPdf($invoice->id);
});
If I try with a full mock, it works but I don't want to mock everything like the method "find".
Do you have an idea of this problem?
I found this issue but I don't really find a solution to this problem.
Thank you in advance for your help.

Related

Testing Laravel View Composers with Mockery

I am trying to test my View Composers. Whenever I pass an object to the $view->with('string', $object), my test fails. This is when I do the test like this:
$view
->shouldReceive('with')
->with('favorites', $this->user->favorites(Ad::class)->get())
->once();
I'm pretty sure this is due to strict checking. So I looked around and saw this issue. However, I can't seem to get it working. The closure return true, but the test fails:
Mockery\Exception\InvalidCountException : Method with('favorites',
< Closure===true >) from Mockery_3_Illuminate_View_View should be called
exactly 1 times but called 0 times.
Here is my current test
public function it_passes_favorites_to_the_view()
{
$this->setUpUser(); // basically sets $this->user to a User object
Auth::shouldReceive('user')
->once()
->andReturn($this->user);
$composer = new FavoritesComposer();
$view = Mockery::spy(View::class);
$view
->shouldReceive('with')
->with('favorites', Mockery::on(function($arg) {
$this->assertEquals($this->user->favorites(Ad::class)->get(), $arg);
}))
->once();
$composer->compose($view);
}
FavoritesComposer class:
public function compose(View $view)
{
$user = Auth::user();
$favorites = $user
? $user->favorites(Ad::class)->get()
: collect([]);
$view->with('favorites', $favorites);
}
How do I test object like this?
I fixed the issue by replacing $view->with('favorites', $favorites); with $view->with(['favorites' => $favorites]); and then testing it like this:
$view
->shouldReceive('with')
->with(['favorites' => $this->user->favorites(Ad::class)->get()])
->once();
So, essentially using only one parameter in the with()-method is what fixed it for me.

Passing a variable from Eloquent to Controller

Is it possible to pass a variable from Eloquent to Controller?
I have here my code for Eloquent where i get the result of my filtered query and i wanna use that same variable to my Controller so that i can use it for my Exporting to excel.
Here's my code for my eloquent model:
$result = $query
->orderBy('id', 'desc')
->paginate($perPage);
if ($search) {
$result->appends(['search' => $search]);
}
if ($emp_status) {
$result->appends(['emp_status' => $emp_status]);
}
if ($company) {
$result->appends(['company' => $company]);
}
if ($age) {
$result->appends(['age' => $age]);
}
if ($tenure) {
$result->appends(['tenure' => $tenure]);
}
if ($benefit) {
$result->appends(['benefit' => $benefit]);
}
if ($level) {
$result->appends(['level' => $level]);
}
if ($gender) {
$result->appends(['gender' => $gender]);
}
if ($civil_status) {
$result->appends(['civil_status' => $civil_status]);
}
if ($birthmonth) {
$result->appends(['birthmonth' => $birthmonth]);
}
return $result;
How can I get "$result" and use it in UsersController?
Sounds like you need to use something like a query scope in your model
So something like this in your model:
public function scopeWhatever($query)
{
return $query->
...
your conditions
...
}
Then in your controller use:
YourModel::whatever()->get();
I would suggest you add the function to the controller, and call it there. But if for some reason, you need it in the Eloquent class, I can suggest one of these two:
make a new instance of your model without saving it, and call the method with it.
(new Model())->myFunction();
this will work for any instance of the model.
myInstanceOfTheModel->myFunction();
the better solution(in my opinion), is using a trait. write a trait with that function and add it to both your model and controller.
`
trait QueryFunction(){
theQuery(){
//your function comes here
}
}
class SomeController extends Controller{
use QueryFunction;
}
class YourModel extends Elloquent{
use QueryFunction;
}
`
Try this:-
Model::query()->where($yourConditions)->get();

Calling same eloquent statement in several controllers

I have an eloquent statement like this:
$constraint = function ($query) {
$query->where('session', Session::getId());
};
$selectedImages = ImageSession::with(['folder' => $constraint])
->whereHas('folder', $constraint)
->where('type', 'single')
->get();
Which I need to call in several controllers.
How is the best way to do it without putting this code every time?
Should I put this code in the Model? but how I put the ImageSession::with if it is inside the same model that has ImageSession class?
In the controller do I have to write...
$imageSession_table = new ImageSession;
$selectedImages = $imageSession_table->getSelectedImages();
Well there are several solutions to this, but one rule that I have learned is whenever you are doing copy paste in the same file it means you need to create a function to encapsulate that code.
The same applies when you are copying and pasting the same code over classes/controllers it means you need to create a class that will have a method, that will encapsulate that code.
Now you could in fact change your model and this depends on your application and what kind of level of abstraction you have.
Some people tend to leave the models as pure as possible and then use transformers, repositories, classes whatever you want to call it. So the flow of communication is something like this:
Models -> (transformers, repositories, classes) -> Controllers or other classes
If that's the case just create a ImageSessionRepository and in there have your method to get the selected images:
<?php namespace Your\Namespace;
use ImageSession;
use Session;
class ImageSessionRepository
{
protected $imageSession;
public function __construct(ImageSession $imageSession)
{
$this->imageSession = $imageSession;
}
public function getSelectedImages($sessionId = false){
if(!$sessionId){
$sessionId = Session::getId()
}
$constraint = function ($query) use ($sessionId){
$query->where('session', $sessionId);
};
$selectedImages = ImageSession::with(['folder' => $constraint])
->whereHas('folder', $constraint)
->where('type', 'single')
->get();
return $selectedImages;
}
}
Then on your controller you just inject it:
<?php namespace APP\Http\Controllers;
use Your\Namespace\ImageSessionRepository;
class YourController extends Controller
{
/**
* #var ImageSessionRepository
*/
protected $imageSessionRepository;
public function __construct(ImageSessionRepository $imageSessionRepository)
{
$this->imageSessionRepository = $imageSessionRepository;
}
public function getImages()
{
$selectedImages = $this->imageSessionRepository->getSelectedImages();
//or if you want to pass a Session id
$selectedImages = $this->imageSessionRepository->getSelectedImages($sessionID = 1234);
//return the selected images as json
return response()->json($selectedImages);
}
}
Another option is adding that code directly into your Model, using scopes, more info here
So on your ImageSession Model just add this function:
public function scopeSessionFolder($query, $session)
{
$constraint = function ($constraintQuery) use ($sessionId){
$query->where('session', $sessionId);
};
return $query->with(['folder' => $constraint])
->whereHas('folder', $constraint);
}
And on your controller just do this:
$selectedImages = ImageSession::sessionFolder(Session::getId())
->where('type', 'single')
->get();
Or you can include everything in your scope if that's your case
public function scopeSessionFolder($query, $session)
{
$constraint = function ($constraintQuery) use ($sessionId){
$query->where('session', $sessionId);
};
return $query->with(['folder' => $constraint])
->whereHas('folder', $constraint);
->where('type', 'single');
}
And then again on your controller you will have something like this:
$selectedImages = ImageSession::sessionFolder(Session::getId())
->get();
Just a side note I haven't tested this code, so if you just copy and paste it it's possible that you find some errors.

Laravel 4 Framework - Query scope not working

Im a big 'ol newbie at Laravel, and im trying to do a query scope but it doesnt seem to be working, i keep getting this error
Argument 1 passed to Letters::scopeForUser() must be an instance of User
My user IS logged in, but it still doesnt seem to be working.
This is my Letters model
<?php
class Letters extends Eloquent {
protected $table = 'letters';
public function scopeForUser(User $u)
{
return $query->where('userid', '=', $u->id);
}
}
and in my controller i have the following
Route::get('myletters', array(
'before' => 'auth|userdetail',
function()
{
// Grab the letters, if any, for this user
$letters = Letters::forUser(Auth::user())->get();
$data = [
'letters' => $letters
];
return View::make('myletters', $data);
}
));
Any help would be greatly appreciated.
Cheers
You should pass a variable $query as the first argument to your method in the Model. For example:
public function scopeForUser($query, User $u)
{
return $query->where('userid', '=', $u->id);
}
The first argument doesn't necessarily need to be $query, but it should be the same variable that you are using inside the scope method ($query in this case).

Laravel: Can't access methods in Model from Controller

I'm having an issue with one of my controllers accessing the methods within a model.
I have a controller (application>controllers>event.php) with an index method:
class Event_Controller extends Base_Controller {
public $layout = 'layouts.default';
public $restful = true;
public function get_index()
{
$category = (isset($_GET['category']))? $_GET['category'] : NULL;
$date = (isset($_GET['date']))? $_GET['date'] : NULL;
$county = (isset($_GET['county']))? $_GET['county'] : NULL;
$events = Event::get_event_list($category, $date, $county);
$this->layout->title = 'Events';
$this->layout->nest('content', 'event.index', array(
//'data' => $some_data
));
}
}
And the model (application>models>event.php):
class Event extends Eloquent{
public static function get_event_list($category = null, $month = null, $county = null)
{
$events = DB::table('events');
if($month)
$events->where('dtDateTime', 'LIKE', $month.'-%');
if($category)
$events->where('strCategories', 'LIKE', '%['.$category.']%');
if($county)
$events->where('strCounty', 'LIKE', '%'.$tag.'%');
return $events->order_by('dtDateTime', 'DESC')->get();
}
}
If I change the model name in the call to the method in the controller (ie Eventsx::....) I get an error that the model doesn't exist so I know it can find the model, however when I try and run this (or with a call to any other method in the Model), I get the error:
Call to undefined method Laravel\Event::get_event_list()
I have similar controllers accessing similar methods in their models but for some reason, it is not playing ball here. The controller can access methods in other Models with no issues.
Here are my routes:
Route::get('/events', array('as' => 'event', 'uses' => 'Event#index'));
Route::get('/events/(:any)', array('as' => 'event', 'uses' => 'Event#event'));
Can anyone see anything glaringly obvious that I'm doing wrong here?
Thanks
I worked it out. class Event is already taken by laravel. I renamed my model and everything worked fine

Resources