Laravel 4 - dependency injection based on configuration? - laravel

I have an interface
interface RecordsService {
public function getRecords();
}
and two implementations:
public class ApiRecordsService implements RecordsService {
public function getRecords() {
//get records from api
}
}
public class DbRecordsService implements RecordsService {
public function getRecords() {
//get records from db
}
}
Now, in my controller I do DI like this:
class RecordsController {
private $recordsService;
public function __construct(RecordsService $recordsService) {
$this->recordsService= $recordsService;
}
}
And I bind it like this:
App::bind('RecordsService', 'ApiRecordsService');
Now, my question is, is it possible to implement this more dynamically, based on configuration, something like this:
switch( Config::get('config.records_source') ){
case 'db':
App::bind('RecordsService', 'DbRecordsService');
break;
case 'api':
App::bind('RecordsService', 'ApiRecordsService');
break;
}
and more important, is this a good practice ?

You can use for that an annonymous function like that:
App::bind('RecordsService', function() {
switch( Config::get('config.records_source') ){
case 'db':
return new DbRecordsService;
case 'api':
return new ApiRecordsService;
}
});

Related

Java : set generic parameters for a method

I have a method that can take ResponseEntity as parameter.
private ResponseEntity<OfferRest> mappedOfferByImagesEnabled(
ResponseEntity<OfferRest> offerResponse) {
for (OfferDetailImageRest image :
offerResponse.getBody().getOfferDetail().getImages()) {
if (image.getDisabled()) {
return offerResponse;
}
}
return null;
}
I have the same method with another parameters: OfferEnity and I don't have need to call getBody() like the other one.
private OfferEntity mappedOfferByImagesEnabled(OfferEntity offerEntity) {
for (OfferDetailImageEntity image :
offerEntity.getOfferDetail().getImages()) {
if (image.getDisabled()) {
return offerEntity;
}
}
return null;
}
My idea is to have a method with one (generic) parameter. Basing on the settings instance I will run the convenient code.
My question, How can I do it?
You can create a method with OfferDetail parameter
public boolean isImageDisabled(OfferDetail offerdetail) {
return offerdetail.getImages().stream().anyMatch(Image::getDisabled));
}
And the use it
isImageDisabled(offerEntity.getOfferDetail());
isImageDisabled(offerResponse.getBody().getOfferDetail());

How to pass parameters and set properties while navigating to a new view model (initializeAsync)

I am using the design pattern mvvm, I have a view model locator and a view model base class.
The view model locator finds what view is associated to the view model. I also wrote a navigation service and one of my methods (NavigateTo) takes in a parameter (an object). the method is to navigate to a view model associated with the view.
namespace StudentData
{
public class StudentOverViewViewModel : ViewModelBase
{
private DataSet studentData;
public Icommand getDetails { get; set; }
public DataSet _data
{
get { return studentData; }
set
{
studentData = value;
RaisePropertyChanged(() => studentData);
}
}
public StudentOverViewViewModel (DataSet studentData)
{
this.studentData = studentData;
getDetails = new Command(Details);
}
public async Task getDetails()
{
// api calls done to retrieve data and set studentData to the current student data
await NavigationService.NavigateToAsync<StudentDetailViewModel>(studentData );
}
}
}
For the second view model I have :
namespace StudentData
{
public class StudentDetailViewModel: ViewModelBase
{
private DataSet Data;
public DataSet _Data
{
get
{
return Data;
}
set
{
Data= value;
RaisePropertyChanged(() => Data);
}
}
}
public StudentDetailViewModel(DataSet Data)
{
this.Data = Data;
}
public override async Task InitializeAsync(object navigationData)
{
if(navigationData is DataSet)
{
Data = (DataSet) navigationData; // after the page is initialized, the variables or properties/ models are not updated and is still null
}
}
}
My issue is that in my initializeAsync method in the second view model, I set the value and property for data, but after the method is done it set all the values back to null.
Thank you in advance for your help.
private async Task InternalNavigateToAsync(Type viewModelType, object
parameter)
{
Page page = CreatePage(viewModelType, parameter);
if (page is UserAuthenticateView)
{
Application.Current.MainPage = new CustomNiavigationView(page);
}
else
{
var navigationPage = Application.Current.MainPage as CustomNiavigationView;
if (navigationPage != null)
{
await navigationPage.PushAsync(page);
}
else
{
Application.Current.MainPage = new CustomNiavigationView(page);
}
}
await (page.BindingContext as ViewModelBase).InitializeAsync(parameter);
}

Test code for enum

If I am declaring 2 enums inside my class this way:
public class EnumerationExample {
public enum Season {WINTER,SPRING,SUMMER,FALL}
public enum Month {JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC}
public List<Month> listMonths;
public Month convert (String val) {
for (Month mtObj : Month.values()) {
if (mtObj.name() == val) {
system.debug('The value passed is ' +mtObj);
}
}
return null;
}
public List<Month> seasonMonths(Season s) {
Season seasonObj = Season.SPRING;
listMonths = new List<Month>();
if(s==season.WINTER) {
listMonths.add(Month.DEC);
listMonths.add(Month.JAN);
listMonths.add(Month.FEB);
}
else if(s==season.SPRING) {
listMonths.add(Month.MAR);
listMonths.add(Month.APR);
listMonths.add(Month.MAY);
}
else if(s==season.SUMMER) {
listMonths.add(Month.JUN);
listMonths.add(Month.JUL);
listMonths.add(Month.AUG);
}
else if(s==season.FALL) {
listMonths.add(Month.SEP);
listMonths.add(Month.OCT);
listMonths.add(Month.NOV);
}
return listMonths;
}
}
how do i write test code for this ??
i tried doing this way but it says season variable does not exist at line EnumerationExampleObj.seasonMonths(Season.WINTER);...
#isTest
public class TestEnumerationExample {
public static testMethod void myUnitTest() {
EnumerationExample EnumerationExampleObj = new EnumerationExample();
EnumerationExampleObj.convert('wintery');
EnumerationExampleObj.seasonMonths(Season.WINTER);
system.assertEquals(EnumerationExampleObj.listMonths.get(0) , Month.DEC );
}}
is there any problem with the access modifier or any specific annotations.?
Your problem is not related to testing at all, but to C# basics like scope and syntax (your sample code is full of syntax errors).
To answer your specific question: if you define a public enum inside a class, you have to prefix it with the class name when used outside that class. Example:
var enumerationExampleObj = new EnumerationExample();
enumerationExampleObj.seasonMonths(EnumerationExample.Season.WINTER);

Cast a dynamic attribute after it's posted (Model binding)

What I have is a model which has one of it's attributes dynamic. This dynamic attribute holds one of about 50 different objects. This model is send to a view that dynamic creates the page based on which object is used. This is working perfectly ... the issue is the postback. When the model posts back the modelbinder is not able to bind the dynamic attribute. I was expecting this and thought I would be able to handle it but nothing that I tried works appart from making an action for EACH different objects.
Model
public class VM_List
{
public Config.CIType CIType { get; set; }
public dynamic SearchData { get; set; }
//Lots of static fields
}
This works
public ActionResult List_Person(VM_List Model, VM_Person_List SearchData)
{
Model.SearchData = SearchData;
//Stuff
}
public ActionResult List_Car(VM_List Model, VM_Car_List SearchData)
{
Model.SearchData = SearchData;
//Stuff
}
But what I want is a single action
public ActionResult List(VM_List Model)
{
//Stuff
}
I have tried things like
public ActionResult List(VM_List Model)
{
switch (Model.CIType)
{
case Config.CIType.Person:
UpdateModel((VM_Person_List)Model.SearchData);
break;
default:
SearchData = null;
break;
}
//Stuff
}
and a Custom modelbinder
CIType CIType = (CIType)bindingContext.ValueProvider.GetValue("CIType").ConvertTo(typeof(CIType));
switch (CIType)
{
case Config.CIType.Person:
SearchData = (VM_Person_List)bindingContext.ValueProvider.GetValue("SearchData").ConvertTo(typeof(VM_Person_List));
break;
default:
SearchData = null;
break;
}
but I can't get either to work. Any ideas?
After trying many different things I finally found a way that works.
Action:
public ActionResult List(VM_List Model)
{
//If the defaultmodelbinder fails SearchData will be an object
if(Model.SearchData.GetType() == typeof(object))
{
//Get SearchData as a Dictionary
Dictionary<string, string> DSearchData = Request.QueryString.AllKeys.Where(k => k.StartsWith("SearchData.")).ToDictionary(k => k.Substring(11), k => Request.QueryString[k]);
switch (Model.CIType)
{
case Config.CIType.Person:
Model.SearchData = new VM_Person_List(DSearchData);
break;
case Config.CIType.Car:
Model.SearchData = new VM_Car_List(DSearchData);
break;
}
//Rest of action
//..
}
and for each object make a constructor that accepts a dictionary
public VM_Car_List(Dictionary<string, string> DSearchData)
{
this.Make = Convert.ToInt32(DSearchData["Make"]);
this.Model = Convert.ToInt32(DSearchData["Model"]);
this.Year = Convert.ToInt32(DSearchData["Year"]);
// ETC
}

CodeIgniter and friendly urls

I currently have a set of urls which are derived from their controller names but they're not very url friendly.
For example, is there anyway I change:
example.com/admin/news_manager/add_article
to
example.com/admin/news-manager/add-article
Any help would be greatly appreciated! Thank you.
If its just that one function you can open applications/config/routes.php and add a line something like this:
$route['^admin/news-manager/add-article'] = $route['admin/news_manager/add_article'];
depending on what your other urls are you could come up with a more generic rule.
Here's what I have, put this in your application/core folder as MY_Router.php
It will convert all of the -'s in your url to _'s.
<?php
class MY_Router extends CI_Router {
function set_class($class)
{
$this->class = $this->replace_underscores($class);
}
function set_method($method)
{
$this->method = $this->replace_underscores($method);
}
private function replace_underscores($string){
return str_replace('-', '_', $string);
}
function _validate_request($segments)
{
$this->segments = $segments;
if(empty($this->segments) || $this->controller_is_in_root_folder())
return $this->segments;
if ($this->controller_is_in_a_subfolder())
{
$this->segments = $this->set_directory_and_remove_from_array();
if ($this->at_least_one_segment() > 0 && !$this->default_controller_is_in_subfolder())
{
show_404($this->fetch_directory().$this->segments[0]);
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
if (!$this->default_controller_is_in_subfolder())
{
$this->directory = '';
return array();
}
}
return $this->segments;
}
else
show_404($this->segments[0]);
}
private function at_least_one_segment(){
return count($this->segments) >= 1;
}
private function controller_is_in_a_subfolder(){
return is_dir(APPPATH.'controllers/'.$this->segments[0]);
}
private function controller_is_in_root_folder(){
return file_exists(APPPATH.'controllers/'.str_replace('-', '_', $this->segments[0]).EXT);
}
private function set_directory_and_remove_from_array(){
$this->set_directory($this->segments[0]);
return array_slice($this->segments, 1);
}
private function default_controller_is_in_subfolder(){
return file_exists(APPPATH.'controllers/'.$this->fetch_directory().str_replace('-', '_', $this->segments[0]).EXT);
}
}
You can use URI routing. See the URI routing page in the CodeIgniter docs.

Resources