I want to extend some core classes and controllers via a module but I don't know how. I can do it with overrides but according to the developers this isn't a good way:
The legacy architecture can still be overridden, though. But in
general, we advise against overriding code. It is better to extend it.
But how can I extend it? Is there any code example?
Best regards
In prestashop docs you can see the override.
Put ur class in /modules/my_module/override/classes
or controller /modules/my_module/override/controllers/{front or admin}
I use it on my modules.
An example of my module, overriding a frontcontroller function:
<?php
class FrontController extends FrontControllerCore
{
protected function smartyOutputContent($content)
{
if (version_compare(_PS_VERSION_, '1.7', '<')) {
//do something
} else {
parent::smartyOutputContent($content);
}
}
}
Related
I have a Rest API with Grail 2.5.6 and I am implementing some improvements and best practices. In this case I need to implement the rule:
Use kebab-case for URLs
Bad: /systemOrders o /system_orders
Good: /system-orders
I know that I can configure the UrlMapping file for each method and I solve it. But I understand that there should be some notation on the method that allows me to conigure was URI.
In the grails 4.0 documentation I see the following example:
#Get("/search?limit=25&media=music&entity=album&term={term}")
SearchResult search(String term)
For Grail 2.5.6 is there an alternative similar to #Get?
Finally with trial and error, I found the solution. I leave you the example of how it was and how the controller turned out.
Before:
class LoginController {
static allowedMethods = [userInfo: 'GET']
def userInfo() {}
}
Now:
class LoginController {
static allowedMethods = ['user-info': 'GET']
def "user-info"() {} // <-- here is the change
}
By defining the method name in quotation marks I could implement kebab-case.
I'm currently switching from .net framework to .net core 3.1.
Defining Api Controllers inside the namespace is all fine and works.
Now I have the case, that I need to declare the Api Controllers within another class, like this:
namespace Api.Controllers
{
public class MainClass : BaseClass
{
public MainClass()
{
}
[ApiController]
[Route("Test")]
public class TestController : ControllerBase
{
[HttpGet]
public int GetResult()
{
return 0;
}
}
}
}
The result is, that the Api Controller can't be found after calling the "AddControllers" method inside the "ConfigureServices" method of the startup class.
The MainClass is instantiated before the Startup class will be called.
I've tried to change the global route and defining an area like "{area=Test}/{controller=Test}", or set the ApiController attribute above the MainClass, but none of them worked.
Is there a way to get this working?
Looks like the default ControllerFeatureProvider does not treat nested controller types as controller. You can add (don't need to replace) your custom provider to change that behavior, like this:
public class NestedControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
if(!typeInfo.IsClass) return false;
if(typeInfo.IsAbstract) return false;
var isNestedType = typeInfo.DeclaringType != null;
var isPublic = true;
var parentType = typeInfo.DeclaringType;
while(parentType != null){
isPublic = parentType.IsPublic;
parentType = parentType.DeclaringType;
}
return isNestedType && isPublic;
}
}
Then add that provider to the ApplicationPartManager in Startup.ConfigureServices like this:
services
.AddMvc()
.ConfigureApplicationPartManager(appPart => {
appPart.FeatureProviders.Add(new NestedControllerFeatureProvider());
});
If you want to replace the default ControllerFeatureProvider, just find it in the FeatureProviders and remove it. Of course then you need to ensure that your custom one should handle everything just like what done by the default logic, something like this:
//for IsController
return base.IsController(typeInfo) || <...your-custom-logic...>;
NOTE: You can refer to the default implementation of ControllerFeatureProvider to learn some standard logic to implement your own logic correctly. The code above is just a basic example. To me, as long as the classes inherits from ControllerBase and not abstract, they can work fine as a controller to serve requests. There would be no serious troubles except some weird conventions (e.g: class name not ending with Controller is still a controller or some standard attributes applied on the controller class are not working ...).
We should not use nested controller classes. Each controller class should be put in a separate file (as a good practice). However the point of this answer (the most interesting part that I'm pretty sure not many know about, is the use of ControllerFeatureProvider which can help you customize the features set in other scenarios). And really if you really have to stick with your design somehow, you of course have to use this solution, no other way.
Is it possible to extend Yii2's BaseYii class, so I could add a static method similar to Yii::t() like this: Yii::my_super_method()?
Can't really find any documentation about that, maybe I missed it.
This is possible by creating own Yii class (for example in root of your project):
require __DIR__ . '/vendor/yiisoft/yii2/BaseYii.php';
class Yii extends \yii\BaseYii
{
public static function my_super_method() {
// ...
}
}
spl_autoload_register(['Yii', 'autoload'], true, true);
Yii::$classMap = require __DIR__ . '/vendor/yiisoft/yii2/classes.php';
Yii::$container = new yii\di\Container();
And loading it in index.php instead core class, by replacing:
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';
with
require __DIR__ . '/../Yii.php';
But if you want only to add a new method you should probably not do this. Overriding core classes in this way is possible, but this is ugly hack and should be avoided whenever possible. It is better to create own helper with this method than to hacking core classes.
Yes it's possible to extend BaseYii class. show below
namespace app\models;
class ClassName extends \yii\BaseYii
{
public static function my_super_method()
{
......
Here your code
........
}
}
Now access your method like
app\models\ClassName::my_super_method();
Now access t() method
app\models\ClassName::t();
Whats is best? Work with low-level methods receiving some arguments and dealing with it.
Or have a high-level interface in the objects that does exactly whats its name says?
eg.:
Low-level:
<?php
class Html {
public function renderSelect($name, $options) {
//
}
}
class Repository {
public function lists($repositoryName, $keyColumn, $valueColumn) {
//
}
}
# usage
$html->renderSelect('campaign_id', $repository->lists('campaigns', 'id', 'name'));
$html->renderSelect('account_id', $repository->lists('accounts', 'id', 'company'));
High-level:
<?php
class Html {
public function renderSelect($name, $options) {
//
}
public function renderCampaignsSelect() {
return $this->renderSelect('campaign_id', $repository->listsCampaigns());
}
public function renderAccountsSelect() {
return $this->renderSelect('account_id', $repository->listsAccounts());
}
}
class Repository {
public function lists($repositoryName, $keyColumn, $valueColumn) {
//
}
public function listsCampaigns() {
return $this->lists('campaigns', 'id', 'name');
}
public function listsAccounts() {
return $this->lists('accounts', 'id', 'company');
}
}
# usage
$html->renderCampaignsSelect();
$html->renderAccountsSelect();
Notably the high-level option will grow as applications scales, if more entities comes up, more methods will be needed, like: added Sponsor will have renderSponsorsSelect and listsSponsors. But its usage makes the code very smooth to read and we can do different implementations for each method.
What do you think?
Thanks.
Low-level (fine-grained) methods are more reusable. High-level(coarser granularity) methods are easier to use. I think the closer to the user interface layer the more higher level methods are preferred, it can hide implementation details and is easier to read as you metioned.
Use what you call "low-level" in generic API, frameworks. A kind of libraries designed to be used in many other projects in different domains.
Examples: Symfony, Zend Framework.
Use what you call "high-level" in domain models. Projects targeted to solve a specific problem. But instead of calling it high-level, say domain-specific or using ubiquitous language.
Examples: phpBB3, Wordpress, your site generaing funny pictures of cats with customised text.
I'm trying to create a form in Yii that is paged. Each page is for a group of fields. The best way to do this, I figured, would be to extend the ActiveRecord model and add a parameter for the current page. This also allows me to override the rules() methods with my own rules for the form itself.
My problem with this is, Yii doesn't allow us to natively extend models. I had to override the getMetaData() method because Yii runs self::model(get_class($this)) on itself, which ends up breaking everything.
Not only that, but I can't use findByPk because Yii relies heavily on the class name.
I know of tons of work arounds to make this work, but I'm wondering if I'm missing something.
Thanks,
Kyle
-- edit --
I totally figured it out, and I totally feel stupid. All I had to do with overwrite the model() method to return the current model class.
So:
class MyAr extends CActiveRecord {
public static function model($class = __CLASS__){return parent::model($class);}
}
class ExtendedForm extends MyAr {
public static function model($class = __CLASS__){return parent::model($class);}
}
Yeah ... I feel stupid.
To extend an ActiveRecord model, simply extend it! All you need to do is override the model method:
class MyAr extends CActiveRecord {
public static function model($class = __CLASS__){return parent::model($class);}
}
class ExtendedForm extends MyAr {
public static function model($class = __CLASS__){return parent::model($class);}
}
The model classes are subclasses of ActiveRecord, so you can override some vars/methods of ActiveRecord whitin them. What I mean is that there's no need to extend Activerecord... again! It is extended when defining models! So just add what whatever you need in there. Of course, this way has the inconvenience of adding the code manually in every model class. If it is too much, and if you're using gii, then you could just modify the code template that gii uses to generate the models.