Lets say I'm building a laravel package, which has hierarchy like this:
PackageContainerClass > *PackageClass* > SomePackageClassMethod()
I want to make PackageClass swappable by a class from main App.
I know I could create a publishable config/package_config.php where the user could override PackageClass location and then use it for creating PackageClass object in the package. But I have quite a few classes to be 'swapped', and dont really feel that its the right approach. What's the best approach for this, is there any Laravel magic I dont know about?
Related
I started creating a laravel 5.8 based modular API framework for our company which should be extended using composer packages.
Now I stumbled over the problem to test each package by itself (each package has it's own GIT project of course) if the package needs to have access to the User model given in the base framework (App/Models/User).
There will be various packages naturally depending on the User model such as specific auth modules.
Unfortunately testing also gets more complex because we are using GraphQL (Lighthouse).
So how should this be done? I tried mocking App/Models/User with a User model contained in the tests folder of my package, but this did not work as expected:
$this->userMock = \Mockery::mock('CompanyName\\PackageName\\Tests\\User');
$this->app->instance('App\\Models\\User', $this->userMock);
When, after that, posting a GraphQL request the resolver method throws a Class App\Models\User does not exist error.
I am quiet new to testing with phpunit so maybe I am just missing something here?
Edit:
I just found out that the error message above is displayed because the User model is also referenced within the GraphQL schema file.
So I there is any solution out there it has to somehow "emulate" the not existing User model class for the whole request lifecycle I guess...
Ok I finally solved my problem which was more conceptual wise I guess. As the user model is pretty strongly tied to the (core) package I want to test, I have now moved the model into the package itself and removed it from the base project.
This has the advantage that the "end user developer" doesn't even see and has to cope with the user model which is handles by the package anyway.
Now I can test the package independently and only have to put a line of documentation into the README to tell, that a user has to change the auth.providers.users.modelvalue to let laravel use the appropriate model (e.g. CompanyName\\PackageName\\Models).
If there will be other packages extending the user model, they will have to depend on the core package (which they should either way) and can extend the model class and tell the user to update auth.providers.users.model again. This way it is also quiet transparent to see which user model is used currently.
For the GraphQL / Lighthouse part I have added the following code to the boot method of the package's service provider to make lighthouse know about new models within the package automatically:
$lighthouseModels = config('lighthouse.namespaces.models');
array_push($lighthouseModels, 'CompanyName\\PackageName\\Models');
config([
'lighthouse.namespaces.models' => $lighthouseModels
]);
This can be repeated for every package adding models as well so lighthouse knows about all of them.
from the official docs
https://laravel.com/docs/5.4/facades
this seems easy to test (the cache class)
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
as well in the documentation of facades it is written:
When building a third-party package that interacts with Laravel, it's
better to inject Laravel contracts instead of using facades. Since
packages are built outside of Laravel itself, you will not have access
to Laravel's facade testing helpers.
I really don't see how this is true. The package very well will access functions of laravel, so I don't see how it should not work with testing?
now in contracts https://laravel.com/docs/5.4/contracts they go on a little philosophical discussion what to use, facades or contracts. Isn't it better to use them together? Because contracts are nothing else than an interface. Now the idea of an interface is not new at all. I don't really get, what is the point about first of all comparing facades and contracts when:
facades are basically a extended class of laravel adding testing possibilities. They hide the implementation though and make it harder to read what functions are available on the class. E.g. you always first have to figure out what implementation is used of the facade, to see what methods there are.
contracts on the other hand are nothing else than interfaces. Basically the laravel people telling us "use interfaces". I agree, interfaces are great. But I don't see in what way this relates to facades. They are not related. They are not interchangeable neither.
So what is this all about?
why say "use contracts OR facades" they are not related, and should be used together imho.
An example is the Mail facade:
/**
* #see \Illuminate\Mail\Mailer
*/
class Mail extends Facade{...
so accessing Mail:: will return an instance of \Illuminate\Mail\Mailer
lets look at \Illuminate\Mail\Mailer
class Mailer implements MailerContract, MailQueueContract
{
nothing else than a class implementing a contract aka interface.
What exactly are they trying to tell us?
third-party package
is key term here to consider. Not every package is used with Laravel only.
The package very well will access functions of laravel
Is that true if I use CodeIgniter? Nope.
Now if you build a package specifically for Laravel, then by all means, use facades all day.
The whole point of this is to not couple yourself tightly with a single framework.
I'm new to Laravel & I wanted to know something about it's feature that calls Contracts.
(If my question not in place, let me know why, and don't just downvote it).
So from what I red in Laravel Documentation and say on Laracasts videos, I understood that contracts they are only interfaces for class implementation.
So what it's good for? That if I or someone else will implement those interfaces will all need to go by the interface and then I dont need to change my code at all?
Is that the reason why Laravel uses it's implementation as a contracts ?
Also I wanted to know, to achive the implementation I must bind the implementation to a contract?
Yes, I think your understanding is mostly correct. I will try to explain with an example. Let's say you have a PackageDeliveryServiceContract that has some methods like trackPackage, getShippingCost.
You create a FedexDeliveryService to adhere to the contract and implement those methods.
In your controller, you can just inject PackageDeliveryServiceContract and start using it right away. (are you familiar with laravel's dependency injection?).
Let's say later you decide you no longer want to ship with Fedex and use UPS instead. Then you can create UPSDeliveryService that also adheres to that contract.
Now, all you need to do is change your binding from FedexDeliveryService to UPSDeliveryService and you don't need to make any changes to your controller code.
Typically you will create the binding between contract and implementation inside a service provider such as app/Providers/AppServiceProvider.php
I'm starting a new project and I want to reuse some parts of it, mainly the stuff related to user registration and authentication. I can copy and paste all of the code but I want to use again. I know there is Package Development in Laravel but it's not easy and feel like there must be a better way.
Some days ago I find a pingpong/modules but I don't know about it. It's third party plugin and don't trust it.
Use this plugin is true? Is this plugin is updated later? What's different between Embedd Package Laravel and pingpong/modules? or Do you have any suggestion?
Pingpong modules seems to be build for the earlier version of Laravel 5 and in how far they are compatible with future versions (and maybe current 5.1.11) I cannot say.
There isn't much activity going look the commit history for 2.1, as of today(18 dec) the last commit was over 6 months ago.
But is the package specifically designed for Laravel? It seems to. They offer a bunch of features which are useful for development. The only unfortunate thing is you get a LOT of code within your own git environment (is it a good thing? I don't know, what do you prefer).
Personally I don't like it in this way for development, I prefer them in the vendor/ folder else it's a pain to update it to newer a version.
Since Laravel 5 Taylor wanted to make package development not too specific anymore, like in Laravel 4. The only thing what you can do (but not have to) to make your package using Laravel is using the ServiceProvider's. The ServiceProvider is the bootstrap into the Laravel application.
If you want to extend or implement your own functionality, fork the repo and build it yourself on top off it and host it (through github/packagist or a private repo using Satis).
Pingpong modules (2.1) is build for Laravel 5 and they you described (Embedded Laravel Package) is more for Laravel 4, because the more specific way you have to write the package.
But, there is alternative?
Whenever you want a more active project/package for development you should tryout Asgard CMS. They are pretty modular and I thought I read somewhere it was inspired by this package (totally not sure).
How about building yourself?
Of course you can build your own packages to achieve the same result. And create it as modular as you want. I created a lot modules for my company and we can create pretty easy a entire system and using and extending/overriding modules. Even small parts from a module can be overwritten to project specific needs.
We have chosen for almost the same structure as the app/ folder which Laravel projects, in case of CMS/API modules.
A packages look like:
tests/
src/
Acme/
Controllers/
Requests/
Models/
Module.php // contains some specifc calculations for example
ModelServiceProvider.php
composer.json
In the composer.json file we autoload: "Module\\": "src/"
And in the config/app.php we register the ModuleServiceProvider. Now we injected the functionality into Laravel's container and can we use it through the app() instance.
But whenever we only want to use the Models with in another project or standalone, we can still use it because the autoloaded features from composer and the way we build the package. Possible to use:
<?php
require_once __DIR__ .'/vendor/autoload.php';
use Module\Models\Module;
$module = new Module;
Edit
The package structure we like to use, to have a section for API or CMS stuff:
tests/
src/
Cms/
Controllers/
Requests/
Api/
Controllers/
Transformers/
Models/
Module.php // contains some specifc calculations for example
Providers/
CmsServiceProvider.php // includes `ModuleServiceProvider`
ApiServiceProvider.php // includes `ModuleServiceProvider`
ModuleServiceProvider.php // contains global stuff like commands etc.
composer.json
and instead of registering ModuleServiceProvider in config/app.php we register the ApiServiceProvider or CmsServiceProvider depending on the wishes of the client/project.
To reuse your classes simply use php namespaces or use to call back your clases.
Using the namespace
namespace Acme\Tools;
class Foo
{
echo "me";
}
You can the call class foo
<?php
$foo = new \Acme\Tools\Foo();
Using Use.
You can also use use Statement as below :
<?php
use \Acme\Tools\Foo;
$foo = new Foo();
Use Middleware
You should also use middleware to filter who should use the scripts ie the Auth middle-ware , which will help you in filtering users , registrations , logins READ MORE http://laravel.com/docs/5.1/middleware
Use Eloquent
Use ORM to create REST apis to your models , its very simple , always let your controller class extend eloquent use Illuminate\Database\Eloquent\Model; ie as :
use Illuminate\Database\Eloquent\Model; .Read More http://laravel.com/docs/5.1/eloquent
Lastly Use Laravel In built Helper functions
There are numerous Laravel In built Helper functions , to use simply go over the documentation to help you
I've used pingpong modules. It a pretty cool package. I'm not sure if it's updated much. But it's a very simple package. The only thing it does is create a folder with almost the same structure as in the app folder + views. But these are modules. You can reuse it if you program them right. The same goes for the other answer from jimmy if you have a good structure you can reuse anything.
EDIT
In the image below you'll see an example of pingpong modules. As you it's pretty much the same structure as the app folder. Maybe more the root folder. Normally it runs start.php and you have a routes.php file int he Http folder. I customized mine a bit. And load the frontend and backend routes within the RouteServiceProvider. This is build with laravel 5.1.
I'm currently following the Laravel Package documentation, which uses the workbench tool to create a standard package tree consisting of controller, config, views, etc. folders. Basically, most folders you would get in a standard Laravel app tree.
However, I had a couple of questions:
Why is the models folder absent here? (though the same goes for tests and commands)
Should I just create the folder myself and add it to the composer.json autoload classmap?
What classes should live inside src/<Namespace>/<PackageName>? I have noticed that a ServiceProvider is automatically created here, but I can imagine most other files just existing in the standard package directories.
Wockbench represents just a tool for creating other tools, that is triggered through CLI. Workbench is very abstract concept.
Model folder is absent simply because you don't need model in every new package. For example, if you are creating middleware package or you own filter package.
Every new class can be added to package dependent on its purpose and responsibility. It can be done in more then one way.
Classes that are general enough to go into every package are:
Package Service Provider
Facade
Basic Class
But it is not a black box. Consider for example request class - it is bound very early in the application life cycle, so no provider is needed.