Yii dependency injection basic - yii

Can anyone explain me DI basics please? I understand what it is, but I don't really know now, how to use DI container in practice. For example, I have 2 functions in the same controller:
public function actionIndex()
{
$productsModel = new Products();
$productsFormModel = new ProductsForm();
$informationFormModel = new InformationForm();
....
}
public function actionInformation()
{
$productsModel = new Products();
$productsFormModel = new ProductsForm();
$informationFormModel = new InformationForm();
....
}
So my two questions is:
As you see above, I use same models in these functions. It is good idea to initialized them into "public function init() {}" and then use them in all class globally or this is bad idea?
I think it should be better if these models would be injected into this controller, right? How to do it correctly?
I was created file DI.php, which I included into entry script. File content was:
<?php
\Yii::$container->set('products_model', 'app\models\Products');
\Yii::$container->set('products_form', 'app\models\ProductsForm');
\Yii::$container->set('information_form', 'app\models\InformationForm');
?>
So then I was able to get class app\models\Products instance globally (in every controller, view or model):
$instance_products = \Yii::$container->get('products_model');
$instance_products_form = \Yii::$container->get('products_form');
$instance_information_form = \Yii::$container->get('information_form');
But this is bad idea, right?
Please, answer someone my two questions. :)
Thanks!

Keeping things DRY is always a good idea. The classes seem very related, so I suggest making this relationship explicit by creating a new model (e.g. ProductsInfo). One could name the controller accordingly (ProductsInfoController), thereby clarifying the application structure.
Use dependency injection sparingly. If there is a different way way, use that instead. DI isn't a good fit for the described use-case.

Related

executing a sql code for creating a table and database in zend framework

I wrote a sql script and in it I created a table ;
Now I need to know ,how I can execute this script? (with which codes?)
And I have another question : where? where I must write this codes?(which folder in zend project?)
if it is possible for you please explain with an example.thanks
Creating tables in the database
Zend Framework is not supposed to be the one creating the tables, thus, my suggestion is to run those scripts in other environment.
The fastest one is, probably, the very own SQL shell, but you can use another software such as MySQLWorkbench if you are using MySQL.
Once the tables are created, the access to the tables is made this way:
Introduction
When you are using Zend Framework, you are making use of the MVC pattern. I suggest you to read what is that: Wikipedia MVC
If you read the Wikipedia link, you probably know now that the acess to the database is going to be made by the model.
Thus, if you followed the recommended project structure that Zend provides you will have a models folder under your application folder. There, you are supposed to implement the classes that will make access to the DB.
But well... you now know where to locate those classes but you will ask me: how? It's easy if you know where to search. ZF provides an abstract class called Zend_Db_Table_Abstract that has all the methods that will make your life easier talking about interaction with your database's tables. This is the class that your classes should implement.
Example
Let's suppose you've got a page in your website in which you want to show to the user a list of products of your local store. You have a table in your database called "products" in which you have all the useful information such us name, price and availability.
You will have a controller with an action called indexAction() or listAction() this action is prepared to send the data to your view and will look like:
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
//TODO: Get data from the DataBase into $products variable
$this->view->products = $products;
}
}
And your view file will that that products variable and do sutff with it.
But now comes the magic, you will have a class that will access to the database as I've said, it'll be like:
class Model_Store_Products extends Zend_Db_Table_Abstract{
protected $_name = 'products';
public function getAllProducts(){
$select = $this->$select()
->from(array('P'=>$this->_name),
array('id', 'name', 'price', availability));
$productsArray = $this->fetchAll($select);
return $productsArray;
}
}
And ta-da, you have your array of products ready to be used by the controller:
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
$model = new Model_Store_Products();
$products = $model->getAllProducts();
$this->view->products = $products;
}
}
It can be said that, since fetchAll is public function, and our select does basically nothing but set which columns do we want (it doesn't even have a where clause), in this case, it would be easier to call the fetchAll directly from the controller with no where and it will recover the whole table (all columns):
class Store_ProductsController extends Zend_Controller_Action {
public function indexAction(){
$model = new Model_Store_Products();
$products = $model->fetchAll();
$this->view->products = $products;
}
}
Thus, our function in the model is not even needed.
This is the basic information of how to access to the database using Zend Framework. Further information of how to create the Zend_Db_Table_Select object can be found here.
I hope this helps.

Default Criteria For Active Record

I have a following question about the best practice for ActiveRecord usage.
My case:
I have a User model which is a normal CActiveRecord.
In many cases I want to have lists of "active" users, defined in the database by WHERE condition "is_active = 1". Besides I want functions find(), findByAttributes(), findByPk() etc. to return the result only if the user is active (for example in "Password request" scenario).
I can always apply this WHERE condition explicitly before using find() functions but I'm searching a way to implement it with less code.
I came to the idea of creating a child class called UserActive and change its constructor like this:
function __construct($scenario='insert') {
parent::__construct($scenario);
$criteria = new CDbCriteria();
$criteria->condition = "is_active = 1";
$this->setDbCriteria($criteria);
}
But I'm not sure if this is a good practice to do this (Since CActiveRecord's constructor asks "Do NOT override the constructor unless it is absolutely necessary!"). Can anyone give advices for this situation?
Try this in your model.
public function defaultScope() {
return array(
'condition'=>'is_active = 1',
);
}
Or define other scope
Yii - using relations with scopes defined in the relation
That's right, you should never override __construct().
You can use model scopes for that. See http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes

zend acl dynamic assertions : when/how to load the resource?

i'm creating a zend framework 2 application and i'm sort of trying to implement what is explained here:
http://ralphschindler.com/2009/08/13/dynamic-assertions-for-zend_acl-in-zf
The demonstration that the code works is really nice, but it doesn't really apply to how a framework (utilizing mvc) works. Or maybe i'm just on the wrong track...
i've created a RouteListener like this :
class RouteListener implements ListenerAggregateInterface
{
public function attach(EventManagerInterface $events)
{
$this->listeners[] = $result = $events->attach(
MvcEvent::EVENT_DISPATCH, array($this, "checkAcl"), 100
);
}
}
the method checkAcl then checks if you're allowed to do what you want to do.
The resource and action are determined like this:
$resource = $routeMatch->getParam("controller");
$action = $routeMatch->getParam("action");
And the role is determined by the identity stored in the session (which implements Zend\Permissions\Acl\Role\RoleInterface)
Following the example: how do i determine if a user is allowed to edit a certain blog-post?
By the time acl is doing it's checking, the controller hasn't loaded the blogpost yet, so i'm not sure how to approach this. Unless i duplicate the retrieval of the blogpost in the assertion, but that i'm hoping there is a better way.
I'm also using doctrine for my persistence layer and in the end i've solved this problem using doctrine's Lifecycle Events. This allows you to trigger the acl-check whenever you want: when a entity (p.e. a blog-post) is loaded, or saved, etc.

How can I resolve dependency in Castle Windsor Factory?

I read about factories in CastleWindsor but I cannot get it clear. Hope anyone could help me.
I have this typed factory in an MVC4 project.
public interface IOrderProcessorFactory
{
T Create<T>(string ProcessorName) where T : IOrderProcessor;
void Release(object service);
IOrderProcessor GetTakeAway();
IOrderProcessor GetInLocal();
}
this is register this way:
container.Register(Component.For<IOrderProcessorFactory>).AsFactory();
container.Register(Component.For<IOrderProcessor>).ImplementedBy<TakeAwayOrderProcessor>().LifestylePerWebRequest().Named("TakeAway"));
container.Register(Component.For<IOrderProcessor>().ImplementedBy<InLocalOrderProcessor>().LifestylePerWebRequest().Named("InLocal"));
If inside an MVC controller I call the factory in this way.
_orderProcessorFactory.GetTakeAway();
I get the correct one, the one named "TakeAway".
But for this I have to previous know the type. In other words, I want to call the factory get methods and pass a "name" and the factory returns the correct one.
For example in pseudo-code I want this
TakeAwayOrderProcessor processor1 = factory.GetMeProcessorCalled("TakeAway")
InLocalOrderProcessor processor2 = factory.GetMeProcessorCalled("InLocal")
I know I can pass parameters to the constructor but then I will have to select it "manually" with if name is this return this one else...
Is there any way Windsor can do this automatic, like StructureMap do with:
ObjectFactory.GetNamedInstance<IOrderProcessor>("InLocal");
You need a TypedFactoryComponentSelector

NHibernate: Using slightly different hbm mapping files depending on context

One of my applications is a public website, the other is an intranet. The public website runs using a limited security user that must access a certain table through a view, whereas the intranet can access the table itself.
This seems like it would be quite simple to setup using Fluent NHibernate. In my ClassMap I could do a check like this:
public class MyEntityClassMap : ClassMap<MyEntity>
{
public MyEntityClassMap()
{
if (NHibernateConfig.Current.Context == "intranet")
Table("t_MyEntity");
else
Table("v_MyEntity_pub");
... etc
}
}
Is there a simple way of doing this for embedded hbm files? The only method I can think of would be to have two copies of the hbm file, which would be confusing and far from ideal.
Is there perhaps a better way of achieving the same result?
Actually what you ask it is possible. You can actually access the embedded XML files and alter their content before the SessionFactory is build (on Application Start).
Assuming your will choose to reference the "t_MyEntity" in your entities by default here is how you can dynamically change this reference when you want to reference the "v_MyEntity_pub" table instead (the code may not work as it is but you will get the idea):
NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
cfg.AddAssembly(ASSEMBLYNAME);
if (NHibernateConfig.Current.Context != "intranet") //this is how you have stated you distinguish the intranet application from the other one.
{
string[] resourcesNames = assembly.GetManifestResourceNames();
foreach (string resourceName in resourcesNames)
{
StreamReader sr = new StreamReader(assembly.GetManifestResourceStream(resourceName));
string resourceContent = sr.ReadToEnd();
resourceContent = resourceContent.Replace("t_MyEntity", "v_MyEntity_pub");
cfg.AddXmlString(resourceContent);
}
}
ISessionFactory sessionFactory = cfg.BuildSessionFactory();
The above code should be executed only once for the lifetime of your application and only for the intranet application.
Although this is perhaps not the most helpful answer to your problem, I don't believe that this is possible in a mapping file. I also don't think that two hbm files would work for the same name, as it would be unable to distinguish between the two, you would instead have to have two identical objects each with slightly different names and mapping files. Which as you said in your question, would be completely confusing and ideal would simply be a spot on the horizon that you were hoping to, someday, reach.
Why is it that can't access everything directly through the view? I'm assuming there is no writing involved in this process? Is there any way you can change this method of accessing data while still maintaining your security?