How best to build with testing this way.
I would like to test everything to the point of no return. Now I just want to select a BillingDetail for this I have the route
Route::post('/user/billingDetail/select/{billingDetail}', [BillingDetailController::class, 'select'])->middleware(['auth:sanctum', 'verified'])->name('user.billingDetail.select');
and here once my test I have tried:
public function test_can_user_select_billing_detail_as_default() {
$response = $this->actingAs(self::$user)->post('/user/billingDetail/select/', [self::$billingDetail]);
$response->assertSuccessful();
}
the problem here is that the route needs a {billingDetail} but what should I do, I have to pass it with how can I do that best. The route needs the model BillingDetail.
there are various ways to do it, according to me you can:
create the billing detail in your setUp function in order to make it usable by multiple tests in that test class.
add the billing detail to a protected property of the class extended by your test class, (Illuminate\Foundation\Testing\TestCase in Laravel), and then use that property in your tests.
use a dataProvider, if you have multiple billing details.
do as you did, but remember that the parameter should be like
['billingDetail' => $yourBillingDetail].
you can persist a faked billing detail with a factory
BillingDetail::factory()->create()
Related
I am creating an API. In this API I am accessing a (permissions) table from a database multiple times, in middleware as well as in controllers. I was thinking, instead of accessing the database multiple times, why don't I call it once and use it multiple times. After calling it once, I could store it in the cache within a service provider. But I am not sure if it is a good way to go because API routes don't load all the services like session.
There are other ways like storing data into the config. Or create a class and make a facade for it and then call it when ever it is needed. But I am curious if the cache would work in API routes and would it be a good idea?
Okay with the advice of #lagbox I created a dead simple class.
namespace App\Helpers;
use App\Permission;
class Provide
{
public $permissions = [];
function __construct() {
$this->permissions = Permission::whereNotNull('route_name')->get();
}
}
This may vary, it's just a class that will keep some collection data in it. I named it provide to keep it generic, just in case that I could need other data than permission in the future. Of course this class could be more detailed but just for storing and returning permissions it is enough.
Then I bound it as a singleton in my AppServiceProvider to run it only once.
public function register()
{
$this->app->singleton('App\Helpers\Provide', function ($app) {
return new \App\Helpers\Provide();
});
}
and when I need it I call it like
$provide->permissions->toArray()
All the features of the collection are available everywhere from the beginning to the end. Yes that may look like an overkill or an abuse of IoC but this über simple approach is in my case a superb solution.
I need to authorize users on a forum.
So in blade, I have #can('editPost', $post) before showing the form to reply to a topic. My PostPolicy class has a editPost method that validates to true if it's the users own post.
However, the issue appears when I want to do a simple check, like deletePost(). This checks to see if Auth::user()->isAdmin
public function deletePost(User $user) {
return Auth::user()->isAdmin;
// return $user->isAdmin
}
However, this won't even get called, since I'm not passing an instance of Post
My real world application is much more complicated, but I'm using isAdmin as a simple example.
I guess defining $gate->define('deletePost', 'App\Policies\PostPolicy#deletePost'); in AuthServiceProvider could work, but would end up separating my definitions and methods, and ultimately for a large app clutter the AuthServiceProvider
When you register a policy it is the classname that is used to route checks to the class, so in order to get routed to the policy you can just pass the class name of the type you registered it with.
Try using #can('delete', Post::class) and see if that gets you there
refer to
Illuminate\Auth\Access\Gate::firstArgumentCorrespondsToPolicy
EDIT
After a little more diggin I found this
https://github.com/laravel/framework/commit/70f75255808ffc96275e6f2f356616dd2e163434#diff-961368895033e553787b301c3be0e17a
so it looks like if you on version 5.1.23 then you will be able to pass a string otherwise your will need to just pass new Post
In controllers
$this->authorize('<ability>', <Class-With-Rule::class> | <Full-Path-To-Class>);
In Blade view
#can('<ability>', <Class-With-Rule>::class> | <Full-Path-To-Class>)
In Eloquent model
$user->can('<ability>', <Class-With-Rule>::class> | <Full-Path-To-Class>);
Methods Without Models:
Some policy methods only receive the currently authenticated user and not an instance of the model they authorize. This situation is most common when authorizing create actions. For example, if you are creating a blog, you may wish to check if a user is authorized to create any posts at all.
When defining policy methods that will not receive a model instance, such as a create method, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user:
https://laravel.com/docs/7.x/authorization
I need to run some code when one of my models is saved (created/updated) or deleted. What's the best way to do that?
There's three different ways that I'm aware of:
Override the save and delete methods on the model
Add creating/updating/deleting callbacks in the boot method
Bind an observer in the boot method
I haven't seen these compared and contrasted, so I don't know what the differences are. I'm worried that the events won't fire under certain conditions.
For example, in Django, deletes only fire if you delete the models one-by-one, but not in a mass delete.
To be clear, I'm looking for answers that compare and contrast these (or other) methods -- not simply suggest even more ways of doing the same thing.
It's just my opinion for several methods you mention previously.
Override the save and delete methods on the model ( If you override it then next update of Laravel change visibility of method your code does not work again. It would throw Exception or PHP error. You have to modify it to work again )
Add creating/updating/deleting callbacks in the boot method ( exist in Laravel 4 you should check it again in Laravel 5 maybe different implementation using Event and Listener )
Bind an observer in the boot method ( exist in Laravel 4 you should check it again in Laravel 5 maybe different implementation using Event and Listener )
I think you should using Event and Listener provided by Laravel. It maybe still work on next Laravel Update. I assume Event and Listener as minor change area in Laravel and changed maybe just different method implementation.
Laravel should have plan of development assign which part of Laravel will be developed as major change area ( big modification ) or minor change area ( little modification ). If you try to change or override major change area it would can't be used on next Laravel Update.
You can register Event and Listener for save and delete record. Laravel have fireModelEvent method on Model ( Illuminate\Database\Eloquent\Model ) which trigger specific Laravel Event. If you've registered Event, Dispatcher ( Illuminate\Events\Dispatcher ) will execute Listener of Event.
Documentation about Laravel Events:
https://laravel.com/docs/5.3/events
https://laravel.com/docs/5.2/events
I assume you have YourModel as Model then do the following action on the below.
Register Event and Listener. Open app\Providers\EventServiceProvider.php then Add Event and Listener to EventServiceProvider.listen properties for YourModel or follow Laravel Documentation to create event using other way.
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
...
'eloquent.saved: App\YourModel' => [
'App\YourModel#eventSaved',
],
];
}
Adding eventSaved method on App\YourModel as Listener for Event so you can do specific action after save or delete.
class YourModel extends Model
{
public function eventSaved(){
// You can add your code to catch save here
}
}
The three methods and 4th referred by #joko. There may be more as well but lets focus on the 4 methods.
Let me describe you them one by one:
1) Override the save and delete methods on the model
In this method you are using OOPD method overriding. You are overriding Laravel's interal save method and adding your additional code by defining your own save method on top of it. This should be avoided as Laravel keep evolving and it may happen that thing start to fail if major change is done like Suppose in future laravel replace save method with any other method to save the records. Then again you will have to create another method to override that new method. Also writing code here may grow your model class file. You model may keep handling things like he shouldn't handle(Example: Sending Email). This method should be avoided.
2) Add creating/updating/deleting callbacks in the boot method
Here you are defining code on the Boot method of the Model. This method should only be used if there is much little code/things that you need to handle on event. The drawback of this method is that it make code more complicated and cluttered as you may write all logic in one like like functional programming. Suppose if you have to do some stuff on before creating and after created. You boot method will grow.
3) Bind an observer in the boot method
This method is pretty good. You create one observer class which handles such stuff that what should happen on Laravel events. It makes code more cleaner and easy to maintain.
Example: Suppose you have to write code in creating, saving, saved, deleting in these methods. In this case, method 1) and method 2) won't be good practice because in
Method 1: We will have to create this 4 methods and override them as well and support them in future releases of Laravel. In this case, code in your Model will also grow because of overriding this methods
Method 2: In this case your boot method will grow as well so you Model file will become a junk of code.
In method 1 and 2 also remember that its not responsibility of your Model to do many of the stuff that you going to write. Like sending email when user is created. These codes you may end up writing in created method.
Suppose now you have scenario where you need to send email to user on created event as well as you need to make user's entry log user in customer CRM. then you will have to write code for both in same method. Probably, you may not following single responsibility principle there. What should we do in the case? See method 4.
4) Other method suggested by #joko
The scenario that i suggested in method 4's end. You may send email to user and log him in Customer CRM whenever it is created. Then your method will do 2 things(Sending email and logging in CRM). It may not following single responsibility principle. What if better we can decouple both of them. Then comes this method.
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'eloquent.saved: App\User' => 'App\Listeners\SendWelcomeEmailToUser'
'eloquent.saved: App\User' => 'App\Listeners\LogUserInCRM'
];
}
Create two listener classes:
class SendWelcomeEmailToUser
{
public function handle(User $user){
// Write code to send email
}
}
class LogUserInCRM
{
public function handle(User $user){
// Write code to log
}
}
Through this you can separate out codes and make them more cleaner.
I generally prefer this method its mode clean. It also gives you much better idea that what actually happen when event happens. It becomes one single point for Event to Listener mapping.
You can create event handlers, for every create/update of model, for example to add to cache the model data which is just saved to database or going to save to database, easier to retrieve without select query call,
while delete call, use forget for given key on cache handler event to delete cache as well as to delete from database too.
I'm partial to doing things manually when you need to know exactly how they're done. I recently used this Laravel Boilerplate to start a project and I like the way they manually fire events in the repository when a model is updated:
https://github.com/rappasoft/laravel-5-boilerplate/blob/master/app/Repositories/Backend/Access/User/EloquentUserRepository.php
Since models should always be updated through the repository, you always get to manually decide how events are handled. You could fire your own event when multiple models are deleted, and act accordingly. All of your options will work though, you just need to find the option that suits your needs best.
You can create abstract Model class that extends Illuminate\Database\Eloquent\Model class and all your model will extend this class. With implementation like this you can have more control on the models. For example
<?php
namespace App\Base\Database;
use Illuminate\Database\Eloquent\Model as BaseModel;
abstract class Model extends BaseModel
{
public function save(array $options = [])
{
//your code here
return parent::save($options);
}
}
You can do this for all the methods of the Model class and also you can add additional methods that are relevant for all models in your application
I am not sure if I am going about this the right way but here is what I am attempting to do, if there is a better way please let me know.
I am using a service provider that pulls some data from a config file. the problem is that if I use Config::set to change one of the settings after calling a function that uses that service provider it will not update. I thought that because I am using app->bind instead of app->share that it would re instantiate the class every time. here is my code:
service provider:
public function Register() {
$app = $this->app;
$app->bind('\path\to\MyInterface', function() use($app) {
$server = $app['config']->get('myconfig.server');
$client = $app['config']->get('myconfig.client');
$key = $app['config']->get('myconfig.key');
$version = $app['config']->get('myconfig.version');
return new MyService(new Instance($server, $client, $key, $version));
});
$this->app->booting(function() {
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('MyServiceFacade', '\path\to\MyFacade');
});
}
Facade class:
class MyServiceFacade extends Facade {
protected static function getFacadeAccessor() { return '\path\to\MyInterface'; }
}
route for testing:
Route::get('test', function() {
$nodes = MyServiceFacade::allNodes();
\Config::set('myconfig.server', 'new server name');
$nodes2 = MyServiceFacade::allNodes();
var_dump($nodes->getContent());
var_dump($nodes2->getContent());
}
);
I am getting the same results from both. shouldn't this be using the update config since I am making a new instance of the controller?
Skip to update 2 below for a stab at the answer
Your question doesn't quite make sense. In your testing route you're saying
App::make('MyController');
This is you asking Laravel to make an instance of the MyController service and/or class. However, you never define a MyController service and/or class.
You bind a \pathto\Interface identifier here
$app->bind('\pathto\Interface', ...
and alias MyService to that identifier here
$loader->alias('MyService', '\pathto\Facade');
but there's no place you bind or alias a MyController identifier anywhere. There's nothing in your code samples that tie MyController to the service you have bound.
Because of that it's not 100% clear what you're asking.
Update: Your question still doesn't quite make sense, and I think this not-sense-masking is what's leading the the unexpected behavior. i.e., you're doing something that "works", as in PHP doesn't complain with an error, but what you think is happening behind the scenes is not happening.
You've refereed to MyService as a facade -- however, you haven't told us what the "facade accessor" string the MyService facade points to (via its getFacadeAccessor method). Also, you appear to be directly instantiating a class from that facade class (new MyService), which isn't how Laravel facades work.
Update 2: The code samples provided are still a little sketchy, and I suspect they don't accurately reflect the actual application. The context from the comments are that MyServiceFacade::allNodes is a call to a facade. However, the facade defined in the code samples is named MyFacade and there's no class MyServiceFacade. I'm going to take a stab based on something mentioned in a comment as to the problem, but based on what I've seen above the problem still might be an incorrect application of service providers, services, and facades.
Binding a service with bind ensures the application container will always return a new instance of the service. I bet if you tried something like the following
$app = app();
$object = $app['\path\to\MyInterface'];
You'd find your object is instantiated anew every time. Adding some basic var_dump debugging to the bound closure and/or service class constructor is a good way to confirm that.
However, Laravel facades are a little different. They're not, technically, a part of the application container system. Facades are a second system built on top of the application container.
In addition to providing a convenient alias for accessing a service class, the facade implementation also forces the service object into a single-instance/singleton irrespective of how you've bound it into the container. This happens in the base facade class here
#File: vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) return $name;
if (isset(static::$resolvedInstance[$name]))
{
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
The base facade class keeps an instance cache in static::$resolvedInstance. The specifics of how Laravel gets here are a bit long for a StackExchange answer, but my Unraveling Laravel Facades article (part of a longer series) is a good place to start.
The problem here (again, based on the incomplete information provided) appears to be a misunderstanding of facades. The main takeaway is a facade always forces a service object to be a singleton.
I am having difficulties testing Controllers in Codeigniter:
I use Toast but when I invoke my Home Controller class I get an exception that "db" is not defined. Has anybody an idea how to test this 1-1?
Thanks
class Home_tests extends Toast {
function __construct() {
parent::__construct(__FILE__);
// Load any models, libraries etc. you need here
}
function test_select_user() {
$controller = new Home();
$controller->getDbUser('foo#gmail.com','password');
assert($query->num_rows() == 0 );
}
}
As others have mentioned, CI doesn't let you call a controller from another controller. The short reason is that controllers always create response headers (even when you don't load any views or call the output class), and you aren't allowed to send two sets of HTTP headers to the browser.
While coding Toast, I tried to hack CI to allow this, but it takes some very hairy hacking of the Loader, and I came to the conclusion that you really shouldn't put any heavy logic in your controllers anyway. IMO, for proper MVC modularity, that stuff belongs in your models, libraries and helpers (which can all be unit tested with Toast).
You might need to edit your database connectivity settings in ../system/application/config/database.php