I've defined a hash salt in my config.yml and would like to get this in my User class, any ideas about how to do this? I've seen loads of examples about how to use this in the controller class, but not in the model?
Thanks
I've had the same question. It'd be nice if there was something similar to sfConfig::get() from symfony 1. Anyway, I think this may actually be a case of "there's a better way to do this". What if you just use setter injection when you instantiate your User class (ie use a setHashSalt() method)? If you're instantiating from a controller you can use $this->container->parameters['hash_salt'].
AFAIK, there's no way to access config.yml parameters without using the container object. I'm very curious to see if anyone has an easier way.
See my answer here:
How do I read configuration settings from Symfony2 config.yml?
with
FIRST APPROACH: Separated config block, getting it as a parameter
SECOND APPROACH: Separated config block, injecting the config into a service
Answering to you, if you want to inject that in the Model, the best approach is to have a Manager that acts as a factory of the model, then the manager can inject itself into the model, so the model can get access it and therefore, get access to the configuration.
Say, your model has a Car and a House if they are related you could have a CityManager with a getAllCars() or getCarById() or similar, as well as a getAllHouses() or a getHouseById() or so.
Then in the CityManager either pass the config to the Model class:
class CityManager()
{
private $myConfigValue;
public getCarById( $id )
{
return new Car( $id, $this->myConfigValue );
}
}
either pass yourself, and let the Model get the config only if it needs it:
class CityManager()
{
private $myConfigValue;
public getCarById( $id )
{
return new Car( $id, $this );
}
public getConfig()
{
return $this->myConfigValue;
}
}
Fill the value as in the article linked.
Related
I have built a small PHP MVC framework and just want to clarify the best way to get data from one model into another. For example:
I have a Users_model that contains a method called get_users().
I also have Communications_model that needs to get specific or all user data and as such needs to access the get_users() method from the Users_model.
Is it best practice to:
a) Instantiate the Users_model in a controller and pass the data from the get_users() method into the Communications_model?
b) Instantiate the Users_model inside the Communications_model and run get_users() from there, so it can be accessed directly?
c) Another way?
Many thanks for any help.
It depends of your motive behind this.
If you want effect on result, then using well know library, like Doctrine etc. should be your choice.
If you want to learn design patterns, then you should get read about ActiveRecord or DataMapper + Repository patterns. Then implements both and check out.
If you want your code, this way - ORM should represent relations of data, then you should ask what it more important? If you menage communication (bus, train), then user can be there assigned and getting users from communication is OK. If user have communication (like car), then relation is reversed.
All depends, what is you motive behind this. Using library, like Doctrine, could you help you running you application. If you want learn design patterns, then check out both options to get some experience.
What you call "users model" is a repository. And what you call "communication model" looks like a service.
Your communication service should have the user repository passed in constructor as a dependency.
I honestly think, that a huge part of your confusion is that you try to call all of those things "models". Those classes are not part of the same layer. You migth find this answer to be useful.
All are possible ways but what I usually do is, whenever there is any function that I think would be reused a number of times by a number of objects, I declare it as static.
It would save the effort of playing with object declaration and would be easily accessible by ClassName::function();
Again, it's a design choice, usually objects are declared right there in the controller and used as per the need but just to save declaration of objects again and again I follow the approach of declaring function static.
The simple principle here is using the __construct() (constructor) to build the object with the relevant properties from the Database. The User Model will have a static function (therefore accessible through any scope) to create an array of instanced objects by simply passing the model data through a new self() which returns the instance.
The concept is you end up with an array of User_Model instances each being a build of the Database columns to properties. All that's left is to create the Database Model and the functions to retrieve the columns and data.
class Communications_Model {
private $_all_users;
public function getUsers() {
$this->_all_users = Users_Model::loadAllUsers();
}
}
class Users_Model {
private $_example_property;
public function __construct($user_id) {
$data = SomeDatabaseModel::getConnection()->loadUserFromDatabase((int)$user_id);
$this->_example_property = $data['example_column'];
}
public static function loadAllUsers() {
$users = array();
foreach(SomeDataModel::getConnection()->loadAllUsers() as $data) {
$users[] = new self($data['user_id']);
}
return $users;
}
}
Of course, now, you have a $_all_users; property that has an array of instanced User Models containing the data.
I've a quick question related to the software architecure. In my application I have a model which contains a method to check the environment the application works in. Let's say the model is called "AppModel".
So, the AppModel::isDevEnv() indicates whether the app is runnig in production or development. It's easy to call this method inside others models, components etc.
The problem is when I want to check the environement inside a view. I created a helper with a propriety method inside just to cover the method from the model and return the result coming from exactly model's method.
class AppModel {
public function isDevEnv() {
return boolean;
}
}
class AppHelper {
public static function isDevEnv() {
$app = new AppModel();
return $app->isDevEnv();
}
}
Is it correct approach? Maybe it's a little bit overcomplicated? Maybe I should just make a static method inside a model and call it whenever I would like to call it?
If this is a legacy system I would recommend to refactor it to the desirable solution. If you want to have this helper or it is a required step for further refactoring then do it.
In general I would inject services which behave differently based on the environment instead of checking the environment inside your models. But it might not be easy with legacy system.
I extended my Laravel4 Project with another Project Model. Now I have two Models (like Model1 and Model2). My goal is to set a custom config var, telling the installation which Project model to use.
Is there a way to set this in the config and then use Project1 as Project in my Controllers?
If I call $p = new Project; I want it to be an instance of either Project1 or Project2, depending on the configuration.
I hope you understand my question, thank you
You can kind of achieve this by using the IoC container.
First, instead of using new Project, use App::make('Project') - that won't change anything yet, but will allow you to now 'redefine' what 'Project' points to.
So the next step is, in your startup script (or a service provider if you're using service providers) code similar to the following:
if (Config::get('model', 'Project') != 'Project') {
App::alias('Project', Config::get('model', 'Project'));
}
This line will bind whatever classname you set in 'model' in the main config to be what the code retrieves when you use App::make('Project'). The reason for the if is just so we don't end up aliasing Project to Project. Feel free to test it without, but it may create an infinite loop/recursion in the IoC.
The code may not be perfect as it's untested, but at the very least it should give you a path to explore.
Points to note
You may find App::bind('project', Config::get('model')); works better, especially if you start using the IoC container's automatic injection
As mentioned above, the if surrounding the magic line may not be required. The IoC container may well be clever enough to not keep following an alias to itself.
A note on static access
I mentioned before to change all instances of new Project to App::make('Project'), but I didn't mention how to change your code when using something like $projects = Project::all();. Well you can still do a similar thing, but you just have to get an instance: App::make('Project')->all();.
When you start going down this route, you find that you're having to App::make('Project') more and more. When this is the case, using Laravel's automatic dependency injection will help a lot. To do this, you typehint the class in your controller's constructor:
class ProjectController extends \Controller
{
protected $project;
public function __construct(Project $project)
{
$this->project = $project;
}
public function index()
{
$projects = $this->projects->whereVisible(1)->orderBy('date', 'desc')->get();
return View::make('projects.index', compact('projects'));
}
}
This automatic injection happens in a few classes. I don't know the full list, but certainly controllers, event listeners, view creators and view composers. If you have a class of your own you wish to take advantage of the automatic injection, you can App::make() it and it too will get its dependencies injected automatically.
I'm trying to get some service (e.g. session) from outside of controller.
Please, explain how to do this right-way.
There are many different ways, all having pros and cons.
First, every service is some kind of object, so you can always just create the object in question yourself. You may be forced to deal with many references, but it's possible. This undermines the idea of Dependency Injection, so it's not what you should do!
The second way is inject the service into your class:
class MyClass{
public function __construct($session){
// ...
}
}
class MyContainer extends Controller{
public function SomeKindOfAction(){
$myClass = new MyClass($this->get('session'));
}
}
This might be ok if you only use the class within your Controller. If you have more nested relations, like in MyClass, you create another class where you need the session, you might run into tight coupling (which is to avoid) as well as complexity issues.
The best way is to create your own service and injecting the things you need there. There are many documentations out there, so I'll just give a short example using MyClass above.
services.yml:
services:
my.myClass:
class: /Acme/DefaultBundle/MyStuff/MyClass
arguments: ["#session"]
Now your Class is as service (who would guess it's that easy!) and you can use it within your controller:
class MyContainer extends Controller{
public function SomeKindOfAction(){
$myClass = $this->get('my.myClass');
}
}
Now you don't have to think about constructor and how to get your objects, the DI-Container will do it for you. If you want to know more, read here.
How can I get configuration values (custom) from anywhere in the app?
I want to do it outside a controller in an entity's method prePersist. Dependency injection sounds illogical here too.
Isn't there some static way of getting the config class or the kernel..?
Dependency Injection is the Symfony 2 way to use configuration: create services for your logic, inject your configuration in services and inject services in other services using OO logic. As for your specific question (using config in the entity's prePersist) the answer is that if you need to access configuration the prePersist callback is not the right place to perform your logic since entities should not be aware of anything that belong to higher software layers (i.e. service/configuration layers).
You can find some more explanation here: How to use the translator service inside an Entity?
What about my own approach of using a custom made ConfigClass? You then should just add it in the needed place and use it.
namespace Your\Namespace\YourConfig;
class YourConfig {
private $energy_config;
public function __construct() {
$this->energy_config = array(
'update_frequency' => 10,
'energy_added' => 10,
'energy_maximum' => 200,
);
}
}
Later if you need the energy_config values, just add in the needed class use statement:
use Your\Namespace\YourConfig;
...
public function foo() {
$config = new YourConfig();
// use your config values
}
This is just my idea, hope it helps until someone gives a truly great solution :)