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.
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.
Please point out any naivete or incorrect assumptions I'm making about Laravel, Composer, PHPUnit, etc.
I had a class called SpeechToTextHelper that was inside a Laravel project, and it used facades like this:
use Cache;
use Log;
use Storage;
Then, since I wanted to share it between multiple Laravel projects, I moved it into a separate repo and required it (into the first project) as a dependency via Composer.
The code all seems to run fine.
My question is different from Using Laravel Facades outside Laravel
What I want to know is:
Now that I also want to write PHPUnit tests for SpeechToTextHelper in my new tools repo, I see errors like RuntimeException: A facade root has not been set. and Error: Class 'Log' not found, presumably because this tools repo has no awareness of Laravel. I guess this means my production code has been working just by side-effect.
In my new tools repo (where my SpeechToTextHelper now is), how am I supposed to indicate (maybe somewhere in composer.json?) that the code will only work if Laravel's facades exist and are initiated properly?
How can I fix my separate repo's code so that its tests can run and also so that it ensures that it can only be "required" by a Laravel project?
P.S. https://laravel.com/docs/5.7/facades says "When building a third-party package that interacts with Laravel, it's better to inject Laravel contracts (https://laravel.com/docs/5.7/contracts) [which live in their own GitHub repository] instead of using facades." "If you are building a package, you should strongly consider using contracts since they will be easier to test in a package context."
But I do not see contracts for Log or Storage at all.
I think you are looking for Laravel component repositories
Cache - This component shows how to use Laravel's Cache features in non-Laravel applications.
Log - This component shows how to use Laravel's Log features in non-Laravel applications.
This video shows, how you can use eloquent outside laravel, I think that will give you better idea.
I'm not positive that this is the best approach, so I'd love if others
would provide better answers.
For production code
My composer.json still has this in the "require" section: "laravel/framework": "5.7.*",.
I plan to only ever require this tools library from within a Laravel app. I'm not sure that this is the right way to make that a rule, but my production code at least seems to be working.
For tests
As for tests, what seems to have been necessary was to add these files from https://github.com/laravel/laravel/tree/2a1f3761e89df690190e9f50a6b4ac5ebb8b35a3:
app/Console/Kernel.php
app/Providers/AppServiceProvider.php
app/Providers/AuthServiceProvider.php
app/Providers/EventServiceProvider.php
app/Providers/RouteServiceProvider.php
bootstrap/cache/.gitignore
bootstrap/app.php
bootstrap/autoload.php
config/app.php
config/database.php
config/logging.php
config/view.php
storage/logs/laravel.log
tests/CreatesApplication.php
tests/TestCase.php
Perhaps those are the minimum set of barebones Laravel files without which tests can't run.
Then I made sure that each test class extended tests/TestCase.php. And I adjusted the namespaces.
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 am trying to use a shared controller from a module in my app, but I'm not really sure how to do it. Here's what I want to do:
I have two revel apps, a frontend and backend app. The frontend app is used to show the user-facing site, and the backend app is for admin stuffs.
I created a special controller to connect to database as per the booking sample.
I want both the frontend and backend app to use the same controller, to minimize redundancy.
From the sample, when you want to have one controller database, it roughly translate to this:
type DBController {
*revel.Controller
}
type App {
DBController
}
This works when I want to have only 1 app, but when I want to share the controller to another app, I can't import DBController to the app.
Things I've Tried
I tried moving DBController to its own package, and then importing that and inherit from it directly:
// in db.go
package controllers
// import and stuffs
type DBController {
*revel.Controller
}
// in app.go
package controllers
import (
dbc "site.com/modules/controllers"
)
type App struct {
dbc.DBController
// *dbc.DBController
}
This gives me a panic error stating that the route is not found:
panic: Route validation error (in /app/path/routes:7):
revel/controller: failed to find controller App
in both inheriting with and without pointer.
I've also tried Revel's module, with the same code, but different directory and importing via config:
// app.conf
modules.dbcontroller=site.com/modules/dbcontroller
And then in app.go:
type App struct {
DBController
}
But it still didn't work with the same error as before. I'm pretty convinced that the right route is by using module, since the documentation said (emphasis mine):
Modules are packages that can be plugged into an application. They allow sharing of controllers, views, assets, and other code between multiple Revel applications or from third-party sources.
A module should have the same layout as a Revel application’s layout. The “hosting” application will merge it in as follows:
Any templates in module/app/views will be added to the Template Loader search path
Any controllers in module/app/controllers will be treated as if they were in your application.
etc..
But I'm not sure how I can share and derive my controller from here.
TL; DR
How do I share controller in Revel so that I can inherit a controller from other module, roughly like:
import dbc "site.com/modules/dbcontroller"
type App struct {
dbc.DBController
}
so that DBController can be used with several revel apps? Thank you very much.
I am not an authority, but I will make a few observations that might help even though I do not have a complete answer for you.
The first thing about your question that struck me is the use of the term "inherit" -- Go does not support inheritance. It does support embedding. I might have written the question subject as "Reuse controller from module in Revel framework.
Second, I wonder if you are trying to reuse a Revel module between two separate Revel applications or if you are trying to reuse code from a module in two separate parts of one Revel application that just happens to have a front end and a back end. A quick read of the reveal framework makes me think modules were designed for the former, not the latter.
Third, I wonder if perhaps you are confusing files with packages. It was not obvious to me when learning Go that one package can span multiple files.. if the same declaration of "package controller" exists in two files such as db.go and app.go, they are still in the same package.
I have a small web application written in Go. It is created a base for a larger system and I would like it to be extendable where components can be added/removed without needing this base to be modified in any way.
The structure is currently:
App
Modules
Core
... Core Files Here ...
app.go
main.go
app.go will contain a routing method which should take a web request and based on the request path know which module is responsible for handling the request. Each module/component having its on controller.
Each component will have its own package name so i think this is going to be impossible since go forces an explicit import.
For example i may add a new module/component called say blog such as:
App
Modules
Core
... Core Files Here ...
controller.go
Blog
... Blog Files Here ...
controller.go
app.go
main.go
There are several ways to achieve your goal. Since Go does not support dynamically loaded libraries at the moment, you need to probably recompile your application whenever you add/remove any components. The simplest way therefore would be a yourapp/core package with the following:
an Application type with an ServeHTTP method that acts as your main application
a Component interface that all your components have to implement. Your might want include a BaseUrl() string and a ServeHTTP method there.
a Register method to your Application type, that can be used to add new components to your app.
Your components can then be implemented in separate packages (e.g. yourapp/blog) and they will probably depend on your yourapp/core package.
The only thing that still needs to be "user-editable" is the main.go file, which might look like this:
func main() {
app := core.NewApplication()
app.Register(blog.Blog{
Title: "My Personal Blog",
})
app.Register(...)
app.Run()
}
Another approach might be to define an RPC interface for your components (which might include functions like RegisterComponent, UnregisterComponent and a GetGlobalConfig).
Then, you can run those components in separate processes which has the advantage that you can start/stop/reload those components dynamically and they can not break your main app. Take a look at the net/rpc package and maybe even httputil.NewSingleHostReverseProxy if you want to use this approach instead.