Replace Yii2 core class - yii2

I am looking to override the default Model \yii\base\Model class with an extended version I have created. For arguments sake lets say I have it in backend\components\Model
I have tried putting both of these in the bootstrap config file but nothing changes - it continues to use the old class
Yii::$container->set('yii\base\Model', 'backend\components\Model');
Yii::$classMap['yii\base\Model'] = 'backend\components\Model';
and within the backend\Model file:
<?php
namespace backend\components;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\UserException;
use yii\helpers\ArrayHelper;
class Model extends \yii\base\Model
{
/**
* Adds a new error to the specified attribute.
* #param string $attribute attribute name
* #param string $key attribute index
* #param string $error new error message
*/
public function addCustomError($attribute, $key, $error = '')
{
$this->_errors[$attribute][$key][] = $error;
}
}
I have also tried adding it to the config file using:
'container' => [
'definitions' => [
'yii\base\model'=>
['class'=>'backend\components\model']
]
],
No matter what I do - when I create a new ActiveRecord, it still holds the same yii\base\model class.
I have spent the better half of the afternoon trying to solve this and have gone through both the Yii documentation as well as the questions already on StackOverflow. There seems to be a recurring theme of the Container function not working and people transitioning to the ClassMap function - yet neither seem to work.
Thanks for the help.

Overriding by container works if you use container to instantiate overridden class (like Yii::$container->get('yii\base\Model');). If you have class MyModel extends \yii\base\Model you're NOT using container, yii\base\Model will be used as base class no matter what, because this is how PHP works and framework has no way to change that. If you want to do this you have at least 4 options:
Preferable: just use class MyModel extends \backend\components\Model for your models and avoid hacky class replacing.
Desperate 1: copy vendor/yiisoft/yii2/base/Model.php to overridden/Model.php, alter its source code, and override autoloading config to use overridden/Model.php for yii\base\Model - add:
Yii::$classMap['yii\base\Model'] = __DIR__ . '/../overridden/Model.php';
or
require __DIR__ . '/../overridden/Model.php';
after
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php
You need to sync all changes from vendor/yiisoft/yii2/base/Model.php to your copy after framework update. In general this should be last resort, since it is really easy to forget about it and get completely unexpected behavior.
Desperate 2: patch vendor/yiisoft/yii2/base/Model.php directly using cweagans/composer-patches. This is easier to maintain than second option, but it still may be surprising after some time. Also, at some point your patch may get conflicts after framework update and someone that knows what is happening will need to take a look at this.
Desperate 3: fork yiisoft/yii2 and do whatever you want - it is your framework now. It may be quite useful for large and active project, but in other case you may end up with completely outdated framework, because nobody cares to fetch changes from upstream.

Related

Get current model alias Yii2 in ActiveQuery init()

I have some models with their ActiveQuery defined with some default conditions.
class ProfileQuery extends \yii\db\ActiveQuery{
public function init(){
$this->andOnCondition(['not',[Profile::tableName().'.status'=>2]]);
parent::init();
}
...
The problem is if in another model search I use a join with an alias the init() function of ProfileQuery still tries to search tablename.status
Is there a way to get the current alias while inside the init() function?
This is not possible right now. Limited alias support was problem since the beginning of Yii 2, but recently it was postponed to Yii 3 and most likely will not be fixed in Yii 2: https://github.com/yiisoft/active-record/issues/33
There were some attempts to fix this, but none of them has been finalized and merged. If you really need it, you may use some code from these PRs and implement it yourself in custom component or fork:
https://github.com/yiisoft/yii2/pull/10253
https://github.com/yiisoft/yii2/pull/10813
https://github.com/yiisoft/yii2/pull/11646
Since Yii 2.0.16 you may also use ActiveQuery::getTableNameAndAlias() but it probably will be useless in init().

ZF2 Configuration Injection Or NOT?

I've googled a lot and read as much as I can on the subject but I cannot find a direct answer to my question anywhere including here. My ZF2 application consists of roughly 7 different modules. 5 of the modules will need access to the same database configuration. The application itself has a database with roughly 124 different tables. So the idea here is to find the proper solution to write the least amount of code considering the setup.
What I'm trying to do is create a specific class to interface with the DB. Where the business logic is kept in the Module and note the controllers to keep everything more abstract and easier to maintain. By that I mean controller X should be able to create a new instance of for instance (Application\Model\DBInterface) and use the models functions to do inserts deletes updates joins selects and so forth. The reason I would like to do it this way is so that all modules installed can use the same interface without having to write endless DI statements everywhere. So what I will need is an example of how I can get the configuration for the DB (currently inside module.config.php + local.php(username / pw)) to be passed to the Application\Model\DBInterface dbConfig variable, and perhaps even an instance of the dbAdapter initialized from config if possible.
Alternatively I could potentially grab the configuration from the Application\Model\DBInterface if such a way exists.
If neither of the above is possible then I can always go back to the old way of doing things by reading an ini file for the db details and instantiating my db adapter that way.
Please keep in mind that I won't be injecting anything in the controllers as the controllers just use $db = new \Application\Model\DBInterface() so injecting into the controllers doesn't make much sense at all.
Is there a better way to do this / optimized / am I doing it completely wrong? Anyone able to share some details please. I've spent way too much time on this already and definitely need help.
Okay, so #Ocramius did just let me know what my misconception with the initializers was and helped me out a bit in understanding it. So here is a probably working solution to your Problem. My understanding about your problem is:
"How to get a DbAdapter set for all your Models implementing a DbInterface". This is how you'd do it:
Step 1: Create invokables for all classes implementing the DbInterface. Create a factory for the default Zend\Db\Adapter\Adapter and then create an initializer for your DbInterface
Module.php getServiceConfig()
return array(
'invokables' => array(
'application-model-one' => 'Application\Model\One',
'application-model-two' => 'Application\Model\Two'
),
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory'
),
'initializers' => array(
'DbInterfaceInitializer' => function($instance, $sm) {
if ($instance instanceof \Application\Model\DBInterface) {
$instance->setDbAdapter($sm->get('Zend\Db\Adapter\Adapter'));
}
},
)
)
The Zend\Db\Adapter\Adapter is using the top-level-configuration-array-key 'db' to automatically inject the dbParams
Step 2: Create your classes implementing your Interface
Application\Model(One|Two|N).php
namespace Application\Model;
class One implements DbInterface, \Zend\Db\Adapter\AdapterAwareInterface
{
/**
* #var \Zend\Db\Adapter\Adapter $dbAdapter
*/
protected $dbAdapter;
public function setDbAdapter(\Zend\Db\Adapter\Adapter $dbAdapter) {
$this->dbAdapter = $dbAdapter;
}
public function getDbAdapter() {
return $this->dbAdapter;
}
// More of your business logic or data here
}
Step 3: Access those classes with the ServiceLocator from your Controllers
SomeController.php someAction()
$dbOne = $this->getServiceLocator()->get('application-model-one');
$dbTwo = $this->getServiceLocator()->get('application-model-two');
// Adapter will automatically be injected
When accessing the invokable from the ServiceManager the initializer will be called. The initializer then will automatically call the Zend\Db\Adapter\Adapter, which in turn get's the parameters from the configuration key 'db'
You may get more information from once the tutorial Application as well as the blog of
samsonasik: ServiceManager Cheat-Sheet

How would Object.defineProperty be in AS3?

I'm an architect from a strong JavaScript background, but I did some .NET and Java in the past.
However, I wanted to put a hand on ActionScript3, which I was promised that is very related to JavaScript.
As a startup project I took on myself to try port to ActionScript3 one of my favorite assertion utils - should.js - that makes your test codes really pleasant to read.
Updated: 2013-02-19
I saw I confuse with my abstract speaking, so I replaced some of the post with the concrete question in mind.
Here's the full picture:
Consider the following JavaScript code:
Object.defineProperty(Object.prototype, 'should'
, { set: function(){}
, get:
function(){
return new Assertion(Object(this).valueOf());
}
, configurable: true
, enumerable : false
}
);
That is part of the implementation of the JavaScript module Should. The other part is a definition of a the class Assertion, that is constructed with a value, and implements a wide and nice set of assertion methods, against that value. Methods like like
var o = Assertion(actualValue)
o.equals(expectedValue1)
o.moreThan(expectedValue2)
o.contains(expectedValue3)
and aliases to keep english grammer
var o = Assertion(actualValue)
o.equal(expectedValue1)
o.contain(expectedValue3)
and aliases for the lazy sharpshooters, like
o.eql(expectedValue)
o.gt(expectedValue) //greater then
o.gte(...) //greater then or equal
//and so on...
and some connectors that just return this, (which is the instance of Assertion constructed with the test value) like
o.be
o.and
What does it give you?
A test code that looks like this:
var person = getPerson();
Should.exist(person); //that's a static call, and that's easy
//but these are a member calls:
person.should.have("name","age","address","friends");
person.name.should.equal("John");
person.age
.should
.be.number()
.and.be.between(20,30);
person.address
.should
.be.string().and
.startWith("\d").and
.endWith(" st.")
//or even
.and.match(/^[0-9]{1,9}\s+[A-Z][a-z0-9 ]* st\.$/);
person.friends
.should
.be.array().and
.be.between(3,5).and
.containOnlyType(String);
Isn't that wonderful? it's plain English!
You could argue about aesthetics of indentation, where to put the and, and if they are at all necessary, but besides that - anybody can read or write it:
Once you took the 'should' attribute that exists on every object but does not spoil map iterations - you can go on chaining whatever you have to claim regarding the value you started from.
It could have more nifty iteration tools, reflection utilities, be augmented with test functions relevant for your object model, and so on and so forth, but lets just get over the first step :)
But for that, you need every object in the system to feature a non-enumerable smart property called should that in it's getter function returns an Assertion object constructed with the this as the tested value.
(you ain't seen nothing yet - wait to see the beautiful rejection messages it gives! Yummie!!
So yea - I would happily sacrifice the option to call an attribute "should"... and will happily give up intelisense as well - at least as long as it's plain English)
So, in comments, bfavaretto gave us the first step - we know how to prevent enumeration of an attribute - great & thanks!!
Now, can we make it a getter-attribute who's function can access the this?
When I'm done I'm going to put it in some public repo licensed under MIT, for all of us to have fun with :)
Help anybody?
You example is actually 90% correct - but define it like actionscript, not like javascript!
You can still define prototypes in AS3 and they will still work just like prototypes in AS2. The only difference in AS3 is the compiler. AVM2 for some reason does not cast prototypes to native classes (although I didn't test custom classes).
The Prototype Trick: Cast the class as an object.
Eg: if you create:
Array.prototype.random = function():void{}
Then create the object:
var myProtoArray:Array = new Array;
2 things will happen:
myProtoArray.random() //ERROR - this will fail, AVM2 did not map the prototype to Array
but
Object(myProtoArray).random() //WORKS
random() was cast to the Object class, then mapped to Array - I have no idea why!
Hope this helps, cheers.
I confess I'm not keenly familiar with how Javascript works, but if I'm understanding defineProperties purpose correctly, it is a runtime dictation of not just what a property should be, but also the associated namespace to which it belongs (or at least what AS3 considers a namespace).
Class properties are either predefined & only modifiable via custom get() set() functions, or dynamic. Once compiled, their namespace cannot be changed (to my knowledge), so any non-private property is implicitly enumerable, and modifiable whether or not you've written getter/setters (ie: foo.a = value). According to Adobe...
Properties that you create are enumerable, but built-in properties are
generally not enumerable.
That said, you can get a complete list of properties from a class by using describeType. Quite an exhaustive amount of info can be gleaned this way, and I suspect should suit your needs if you wanted to port Mozilla's recreated defineProperties example. Below is an example printing out only property values.
function showProps(obj:*):void {
var desc:XML= describeType(obj);
// public vars
for each (var n:XML in desc.variable){
trace(n.#name + ": " + obj[n.#name]);
}
// getters
for each (n in desc.accessor){
try {
trace(n.#name + ": " + obj[n.#name]);
} catch (error:Error) {
trace("Unable to read write-only property.");
}
}
}
I hope this helps, but I'm certain I don't fully understand what you're trying to accomplish. If you could elaborate, that'd be appreciated.
Ok, guys, thanks for all the help, 22+
I'll give a summary for the people that are interested in the original question, and after that - I'll show you the outcome of my efforts.
The challange was made of two parts:
1 - prevent the augmented (=added on runtime) property from being enumerated
To the first part - thanks to #bfavaretto, commented on the question level - Object.setPropertyIsEnumerable - did the trick great.
2 - make the augmented property operate a getter function with access to the this so it can use it on the constructor of the returned value.
About this second part - Basically - I could not find a way to augment (=add) a property getter to a prototype, and have it operate on instances that enjoy it's API through the inheritance tree.
Anyway, within these limits - here's the outcome:
https://github.com/osher/should.as
Not exact porting because of the platform differences,
and I still have some methods to catch up with the original should.js (like the HTTP testing methods)
but close enough.
The main difference is that instead
var o:Object =
{ name : "Radagast"
, color: "Brown"
}
o.should.have.properties("name","color")
.and.have.property("name","Radagast");
o.name.should.not.equal("Palandoo");
o.color.should.equal("Brown");
you have to go
o.should().have.properties("name","color")
and.have.property("name","Radagast");
o.name.should().not.equal("Palandoo");
o.color.should().equal("Brown");
(the brackets - no getter possible - so the should attribute is a method, and you have to invoke it yourself)
Now if you get stuck and need help from the intellisense, you have to do this:
var should:tdd.Should = o.color.should();
should. <ctrl+space>
which kind'a takes the sting out, but for a peek in the intelisense - it helps
Important
One more thing - you have to force the static constructor of Should as soon in execution as you can,
for example, I do it here:
[Suite]
[RunWith("org.flexunit.runners.Suite")]
public class my_awsome_test_suite
{
//forces the static constructor of tdd.Should
import tdd.Should;
private static var s:Should = new Should();
public var c1:testCase1;
public var c2:testCase2;
public var c3:testCase3;
public var c4:testCase4;
}
I'll probably add some propper README.md later, and more awsome member functions to tdd.Should
Have fun

Namespace vars between Classes

Synopsis
How do you declare variables in a namespace while using the use statement? (ie., without declaring the namespace with the variable name)
How do you reference namespace variables with the "use" statement without a container reference. (ie., trace(foo) rather than trace(a.foo) [seems kinda pointless if I have to state this after already switching to the namespace])
Explanation
Having read Grant Skinner's "Complete Guide to Using Namespaces", and other articles, such as Jackson Dustan's "Better OOP Through Namespaces", I'm left with the above unanswered questions. I feel as though I'm missing some basic principle, but I can't seem to get namespaces to work. The following examples are written for use with the Flash IDE, so assume the following...
locus.as
package com.atriace {
public namespace locus = "atriace.com";
}
testA.as
package com.atriace {
public class testA {
import com.atriace.locus;
locus var foo:String = "Apple";
public function testA() {}
}
}
testB.as
package com.atriace {
public class testB {
import com.atriace.locus;
use namespace locus;
public function testB() {
trace(foo);
}
}
}
Document Class:
import com.atriace.testA;
import com.atriace.testB;
var a:testA = new testA();
trace(a.foo); // results in "Apple"
var b:testB = new testB(); // compile error: variable "foo" not defined.
Issue #1
In my mind, a namespace is little more than an object to hold variables that has scope level access. Ergo, global is a namespace visible to all functions (since it's the root scope), local is namespace (specific to the current and child scopes), and so on. If true, then switching to a namespace with use should allow you to simply declare variables that happen to exist in both the local and custom namespaces. For example:
use namespace locus
var bar:String = "test"; // this now *should* exist in both local & locus scope/namespace.
Since I'm unaware of a method to iterate over a namespace like a normal object, I don't know whether this is what happens. Furthermore, I haven't seen any cases where someone has declared a custom namespace variable this way, so I assume namespace variables must always be explicitly defined.
Issue #2
You might ask, "what's the goal here?" Quite simply, we want a dynamic pool of variables and methods that any new classes can add to (within the same package). By switching to this namespace prior to calling methods, we can reduce the wordiness of our code. So, class.method() becomes just method().
In testB.as we'd fully expect an error to occur if we never imported the testA.as class and instantiated it; especially because foo isn't a static member of the class (nor do we want it to be). However, since we've instantiated foo at least once, the namespace locus should now have a variable called foo, which means that when testB.as gets instantiated, and the constructor seeks a value for foo, the namespace already has one.
Obviously, there's a flaw in this thinking since the Flash compiler complains that foo has never been declared, and the only way I can reference foo from the document class is by referencing the container (ie., a.foo rather than just switching to the namespace with use, and tracing foo directly).
For the sake of argument, neither inheritance nor static members are a solution to this dilema. This is both an excercise in learning better code techniques, and an answer to the structure of a large utility class that has complicated dependencies. Given the absence of a variable/method, you could simply code around it.
I know it's not a heavily documented topic, which is why I'm hoping some sage here may see what I'm missing. The help would be much appreciated. :)
"use namespace" is for the consumer side. You always have to include the namespace in any declaration:
MyNamespace var foobar : uint;
If you wish to add namespaced package-global variables (you shouldn't as a general rule), you have to define each one of them in a separate .as file as packages only allow one publicly-visible definition per file at the top-level.
In your example above you are using namespaces incorrectly. A namespace can span multiple classes, but does not achieve the cross-class functionality you are looking for. This is more the domain of aspect-oriented programming.

Is it bad to prefix all of my framework class names?

I develop a lot of frameworks for Flash games and applications. I have always prefixed my class names with a random character or two, to avoid conflict with class names that the developer may already have, for example:
class LEntity
Recently I had a co-worker blast me for poor and "annoying" naming of classes who then proceeded to rename every class in the frameworks I've created for people here to use.
I'm having trouble explaining my reasoning thoroughly enough for him to accept what I've done as a good approach.
Is what I've done above actually a bad thing? If not, how can I explain otherwise? If so, why?
Comments are asking about namespaces - I know AS3 in this example has what I know to be called a namespace but I'm not sure if this is the same thing or if it can be used as expected.
Given that Actionscript supports namespaces, there is no reason to use prefixes simply to prevent naming clashes. That's what namespaces are for.
Some people like to use namespaces to significy member variables (ie, underscore prefix, or sometimes m_) and that has some merit, but simply for the sake of name clashing no.
It sounds like you don't quite understand what namespacespackages are in AS3.
An example:
//Class1.as
package com.test.someModule { //This is the package/namespace
public class Class1 {...}
}
//Class2.as
package com.test.otherModule {
import com.test.someModule.Class1; //Class1 can be used as "Class1" now. Otherwise you would do "com.test.someModule.Class1"
import com.test.someModule.*; //You can also use the "*" to "import" all classes in that package
}
I have to agree with your co-worker, your class names are 'annoying'.
In Actionscript 3 we use the package name to define the namespace of a class. If you're not sure what namespace means, take the wikipedia definition (as of the time of writing):
"In general, a namespace is a container for a set of identifiers
(names), and allows the disambiguation of homonym identifiers residing
in different namespaces."
So you will never "conflict with class names" as long as you name your packages correctly. Most developers use what is called the reverse domain notation to name their packages (e.g com.mywebsite.MyGenericNamedClass). Domain names are unique so it's very unlikely you would clash with another class.
As a rule of thumb the class name should be as descriptive as possible, so some of your class names will be the same as someone else's class. Take the default Sprite class for instance:
import flash.display.Sprite;
import uk.co.mywebsite.Sprite;
if you then initialize an object:
var mySprite:Sprite = new Sprite();
The compiler would not know which Sprite you want to initialize (is it the flash sprite or your own custom sprite), and it would throw an error.
The solution is simple: because your packages have been named properly, all you need to do is to use the full class name including the package name to initialize your object:
var mySprite:uk.co.mywebsite.Sprite = new uk.co.mywebsite.Sprite();
var myOtherSprite:flash.display.Sprite = new flash.display.Sprite();
Mind you, you would rarely need to do that. This is only necessary if you want to use those two classes (the default Sprite and your own Sprite) in the same scope. Generally, you would only import your own class:
/* we are not importing this any more
import flash.display.Sprite;*/
//only importing my own class
import uk.co.mywebsite.Sprite;
/* now I can initialize my object without using the full class name, and the compiler knows
I mean my own Sprite class */
var mySprite:Sprite = new Sprite();