We developed a modular project in Laravel 5.1. There are lots of core modules and models that use these modules. In our case, if the inserted module uses other modules, it will be related to those models dynamically.
When I remove the module from project by hand, I need to remove its dependencies from each module. We want establish relations without creating dependency between modules.
For example;
User model from account module used by other many other modules. Assume we have discussion module.
When we build a relationship for discussion model, we can reach the user of the corresponding model. However, if we establish a relationship with the user model, this project will no longer be a moduler.
We want to add dynamic functions to the user module from discussion module.
Temporarily, we add this code fragment to user module.
/**
* #return mixed
*/
public function lastAnswer() {
if( class_exists( Config::get( 'account.models.discussion' ) ) ) {
return $this->hasOne( Config::get( 'account.models.answer' ) )->latest();
}
return null;
}
If the config file has a relation, we link it, otherwise it will not be linked or we will return null.
But we want to add this dinamically from discussion module rather than account module.
If we accomplish this, whenever we add or remove the discussion module from project, it will continue to run without problem.
We tried to add laravel macroable as a trait but we could't make it work in model files. It gives scope error.
For transformers files we are able to do this but in model files it didn't work.
Related
I am using DarkaOnLine/L5-Swagger package for project. Here is example annotation
/**
* #OA\Post(
* //annotations etc..
* )
*/
public function login(LoginRequest $request): JsonResponse
{
//login method
}
But using annotations in controllers complicates the code. Is there another way to do it in separate file?
Not sure it complicates things - with separate files you always have to update two different places.
Your annotations can be whereever you like as long as they are associated with structural elements (class, method, etc) so reflection can find them (means you need to create dummy classes, etc if the annotations are separate from the "real" code).
Also, those files need to be found by the bundle but I would expect you can configure multiple path to scan.
The downside for me is that you need to replicate all details, in particular type-hints and keep them in sync.
I have made a tournament system in Laravel 5.3.
Now I want to extract the core ( generating trees ) into a plugin, and make it open source.
The idea is doing the plugin, and then replace the code in my app with the plugin's code, and all the references.
I'm making a demo, so people can migrate an seed necessary objects to generate his own tournament tree.
My main concern is that in my system, I have a lot of thing that should not belong to the plugin, but they still are in the same tables.
In my models, I removed a lot of fields / functions that are not necessary in the plugin.
For instance, In my tournament model, I have a function that handle permission, because not all users can "crud" all tournaments. This function has no place in my plugin, as I will not include any policy ( this should up to each use case )
Another example should be printing the tree. In my system, I allow user to print the tree, but in my plugin is meant to be the core functions, not optional stuff.
Also, I think I should use only 1 model, I mean, I should delete my project model, and use my plugin model as they will represent the same data. So what if I remove a field / method as mentioned previously???
For my models, a solution should be that I create child object and just extend my plugins models, but it would mean change all my actual model names, is it a good approach???
How should I manage migration??? Should I include useless fields in my plugin?
Also, which User model should I use, Xoco\my-plugin\User, or App\User
What is the best practice of including models/activerecords within a Yii2 module in a way that they are configurable?
These are just some of the problems we face when we want to use an activerecord included inside a module:
Adding events & behaviors to models/activerecords provided by a module. I want to attach events and behaviors to the models included in a module using Yii2's configuration format. How can this be done?
Defining relations with models/activerecords that exist outside of the module. When linking an activerecord contained inside a module to the User activerecord we can rely on Ỳii::$app->user->identityClass, but for other custom relations we might need to extend the activerecord. Is there any better approach? Extending activerecord classes from modules somewhat defeats the purpose of modularity.
Configuring various other variables within the module/activerecord. Let's say we want to adjust the max string length validation value. In a module Controller, we can always use $this->module->params to read any custom value, but we cannot do this from a Model or an ActiveRecord. What are we supposed to do instead?
I think you might end up using dependency injection:
Write an extension "\common\extensions\MyBootstrap":
namespace common\extensions;
use Yii;
use yii\base\BootstrapInterface;
use yii\base\Application;
class MyBootstrap implements BootstrapInterface {
/**
* #param Application $app Application
**/
public function bootstrap($app) {
Yii::$container->set("common\\modules\\test\\models\\Test1", "common\\modules\\test\\models\\Test2");
}
}
add to your config:
'bootstrap' => [
'common\extensions\MyBootstrap',
],
'components' => [
// ...
]
and in your code you have to use Yii::$container->get():
$test = Yii::$container->get('common\modules\test\models\Test1');
var_dump($test);
which will create Test2 model instead of Test1.
If you want this to happen for your ActiveRecord, override this:
public static function instantiate($row) {
return \Yii::$container->get(static::class);
}
EDIT: The underlying issue has now been resolved. We can use DI to inject relations into ActiveRecords.
As of July 2017, Yii2 does not allow ActiveRecord dependency injection!
See:
https://github.com/yiisoft/yii2/issues/8639
https://github.com/yiisoft/yii2/issues/11575
https://github.com/yiisoft/yii2/issues/5786
https://github.com/yiisoft/yii2/pull/14078
https://github.com/yiisoft/yii2/issues/13779
The only way around this is to configure your modules through your Yii::$app->params and then use those values inside module ARs (eg. when doing validation).
I'd like to get the path to a package public directory (css etc) based on the package alias.
Is there anything already built into the laravel framework?
In other words something like:
public_path('myalias');
When I'm talking about alias, you would typically "alias" a module by adding the following within your service provider's boot method:
$this->package('namespace/package','alias_name');
For those wondering why someone might want to do this:
We are running a multi domain/subdomain application that makes use of a central piece of code for all of the domains and then specific packages per domain (I'll refer to them as funnels).
Each funnel has its own controllers that can possibly extend base controllers to implement their own functionality while re-using code where they can. They also have their own views.
The funnel refers to its own views by way of something like:
View::make('funnel::path.to.view')
The way we accomplish this is by doing some business logic on page load to only load the FunnelServiceProvider related to that particular domain and aliasing it to "funnel". This way our base controllers can also refer to funnel and not be tied to a particular packages views,includes,blocks etc.
My hope is to do something similar on the views so that I can simply call something like get_funnel_path() to get the path to the funnel that is currently being loaded.
The value could then be used to load css,js,images etc without worrying about the funnel path.
This would allow us to simply copy and paste views from one domain to the next without having to modify all of the paths in potentially multiple files. We could also make use of globally included files in all/most of the views.
An example of this might be the head. The head section should be the same for 99% of the files, however the path where it loads its resources should change based on the funnel.
We use the same naming conventions for css files as well as use sass, imports, merging for all of the funnels; so only the path needs to change.
You can do something like this although it will only work with your own packages and require a bit of work. Because the alias is not really stored somewhere you can easily access you have to do that yourself.
First create some kind of class to store your package names in. I called mine PackageManager:
class PackageManager {
private $packages = array();
public function addPackage($fullName, $alias){
$this->packages[$alias] = $fullName;
return $this;
}
public function getPublicPath($alias){
if(!isset($this->packages[$alias])) return public_path();
$path = 'packages/' . $this->packages[$alias];
return public_path($path);
}
}
Now let's register that class as a singleton in a service provider:
$this->app->singleton('packagemanager', function(){
return new PackageManager();
});
Then, in every package you want to register, add this call in the boot method right next to $this->package():
$this->app['packagemanager']->addPackage('vendor/package', 'alias');
After that you can do this anywhere in your application:
app('packagemanager')->getPublicPath('alias');
If you want a shorter syntax, add this helper function somewhere:
function public_package_path($alias){
return app('packagemanager')->getPublicPath($alias);
}
And just do:
public_package_path('alias');
Am having problems understanding where classes should be kept in CI. I am building an application that describes / markets mobile phones.
I would like for all of my functions (i.e. getphone, getdetails etc.) to reside in one class called Mobile - I understand that this file should be called Mobile.php and reside in the controllers folder.
Can I then have multiple functions inside Mobile.php? E.g.
public function getphone() {
xxx
xx
xx
}
public function getdetails() {
xxx
xx
xx
}
Or do I need to put each function in its own class?
I'd really appreciate looking at some sample code that works. I've been going through the documentation and google for a few hours, and tried all sorts of variations in the URL to find a test class, but without much luck! I've even messed around with the routes and .htaccess...
All I am trying to achieve is the following:
http:///model/HTC-Desire/ to be re-routed to a function that accepts HTC-Desire as a parameter (as I need it for a DB lookup). The default controller works fine, but can't get anything to work thereafter.
Any ideas?
Thanks
Actually it works like this:
Controllers and Models go to their perspective folders as you know it
If you want to create functions that are not methods of an object, you must create a helper file. More info here :
http://codeigniter.com/user_guide/general/helpers.html
Now if you want to create your own datatypes (classes that don't extend Models and Controllers), you add them to the library folder. So if let's say you want to create a class "Car" you create this file:
class Car{
function __construct(){}
}
and save it in the libraries folder as car.php
To create an instance of the Car class you must do the following:
$this->load->library('car');
$my_car = new Car();
More information on libraries here:
http://codeigniter.com/user_guide/general/creating_libraries.html
Yes, you can have as many functions in a controller class as you'd like. They are accessible via the url /class/function.
You can catch parameters in the class functions, though it's not advisable.
class Mobile extends CI_Controller{
public function getPhone($phoneModel=''){
echo $phoneModel;
//echo $this->input->post('phoneModel');
}
}
http://site.com/mobile/getPhone/HTC-Rad theoretically would echo out "HTC-Rad". HOWEVER, special characters are not welcome in URL's in CI by default, so in this example you may be met with a 'Disallowed URI characters" error instead. You'd be better off passing the phone model (or any other parameters) via $_POST to the controller.
Classes can exist both as Controllers and Models, as CodeIgniter implements the MVC pattern. I recommend reading more about that to understand how your classes/functions/etc. can best be organized.
Off the top of my head, Pyro CMS is an application built with CodeIgniter and the source code is freely available. I'm sure there are others.
I think it's best you handle it from one perspective, that is; create a utility class with all your functions in it.
The answer to the question of where to put/place the class file is the "libraries" folder.
This is clearly stated in the documentation. Place your class in the libraries folder.
When we use the term “Libraries” we are normally referring to the
classes that are located in the libraries directory and described in
the Class Reference of this user guide.
You can read more on creating and using libraries Creating Libraries — CodeIgniter 3.1.10 documentation
After placing the newly created class in the libraries folder, to use just simply load the library within your controller as shown below:
$this->load->library('yourphpclassname');
If you wish to receive several arguments within you constructor you have to modify it to receive an argument which would be an array and you loading/initialization would then be slightly different as shown below:
$params = array('type' => 'large', 'color' => 'red');
$this->load->library('yourphpclassname', $params);
Then, to access any of the functions within the class simply do that as shown below:
$this->yourphpclassname->some_method();
I hope this answers your question if you have further question do leave a comment and I would do well to respond to them.