I was looking for a way to pass "GET" variables in codeigniter and ended up coming across this :
link text
I am wondering how to implement it.
For example :
www.website.com/query would give me every entry in the DB .
Typically I would have
www.website.com/query/?id=5 to get the equivalent entry.
when i try to do that the CI way :
www.website.com/query/id/5
I get a 404 error since it is looking for a class named id and it can't find it.
is there any way to get a step by step way to do this?
thank you.
Two good ways to achieve this using methods intended by the Codeigniter developers.
OPTION ONE:
If you always expect an "id" parameter to be present you could take advantage of a feature where you pass the value in the URI immediately after the method (function) you want to call.
Example passing /[controller]/[method]/[value]:
http://www.website.com/query/index/5
You would then access the value of "id" as an expected parameter of the function.
Class Query extends Controller {
...
// From your URL I assume you have an index method in the Query controller.
function index($id = NULL)
{
// Show current ID value.
echo "ID is $id";
...
}
...
}
OPTION TWO:
If you would like to allow many parameters to be passed in addition to ID, you could add all parameters as key=>value pairs to the URI segments in any order.
Example passing /[controller]/[method]/[key1]/[val1]/[key2]/[val2]/[key3]/[val3]:
http://www.website.com/query/index/id/5/sort/date/highlight/term
You would then parse all the URI segments from the 3rd segment ("id") forward into an array of key=>value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Query extends Controller {
...
// From your code I assume you are calling an index method in the Query controller.
function index()
{
// Get parameters from URI.
// URI Class is initialized by the system automatically.
$data->params = $this->uri->uri_to_assoc(3);
...
}
...
}
This would give you easy access to all the parameters and they could be in any order in the URI, just like a traditional query string.
$data->params would now contain an array of your URI segments:
Array
(
[id] => 5
[sort] => date
[highlight] => term
)
HYBRID OF ONE AND TWO:
You could also do a hybrid of these where ID is passed as an expected parameter and the other options are passed as key=>value pairs. This is a good option when ID is required and the other parameters are all optional.
Example passing /[controller]/[method]/[id]/[key1]/[val1]/[key2]/[val2]:
http://www.website.com/query/index/5/sort/date/highlight/term
You would then parse all the URI segments from the 4th segment ("sort") forward into an array of key=>value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Query extends Controller {
...
// From your code I assume you are calling an index method in the Query controller.
function index($id = NULL)
{
// Show current ID value.
echo "ID is $id";
// Get parameters from URI.
// URI Class is initialized by the system automatically.
$data->params = $this->uri->uri_to_assoc(4);
...
}
...
}
$id would contain your ID value and $data->params would contain an array of your URI segments:
You can still use GET parameters, they're just mapped to controller member function parameters:
test.com/query/id/4
Would map to the controller:
$query->id($id);
This assumes you have added a query controller and member function properly in the controllers folder in your CI application.
You can also pass your parameter values as POST parameters using a form and the CI input class.
Use $this->uri->uri_to_assoc(2) 2 is an offset, as you starting your associative array of segments in the 2nd segment. You will also need a route to make /query map to a controller and method (unless you do this in the index() method).
So, this URL:
/query/id/foo/key/bar
could be read using:
$get = $this->uri->uri_to_assoc(2);
echo $get['id']; // 'foo'
echo $get['key']; // 'bar'
It's not great, but it works.
Related
I have a common api for search opertaions as well as get operations if the i don't pass the search parameters the URL changes which i don't want.
for eg:
if search parameter is passed:
localhost:8080/api/search?name=harmeet
if no search parameter is passed:
localhost:8080/api/search
what i want is the url should not change whether i pass the parameter or not
for example if i don't pass parameter the url should be:
localhost:8080/api/search?name=''
The code has been written using spring boot
What URL gets called is depended on the client-side and not on the server-side. If you always want a parameter value to be something or "" when it is not passed by the client-side, you can define your controller method as:
#GetMapping("/api/search")
public Object getName(#Requestparam(value="name",required=false,defaultValue="")String name){
// your logic here
// value of 'name' variable will "" if nothing is passed
}
Just set the required field as false and use the defaultValue field to set whatever default value you want to set if the parameter is not provided in the URL call.
As per i understand you want to create API which handle two API calls.
Such as,
http://localhost:8080/search
http://localhost:8080/search?name=hermeet
I supposed you already create API in backend... Right!!!
So, There is not any issue on your logic but parameter which you defined in your method is by default required.
Which don't allow you to pass null or empty value.
You just need to allow your parameter for work with both the cases.
As following ways,
Optional<'Parameter_Type'> parameter_name
#RequestParam(value = "parameter_name", required = false) String variable
Example :
#GetMapping("/search")
public List<Entity> getSearchResult(#RequestParam(value = "parameter_name", required = false) String variable) {
if(variable!=null) {
// Get Data based on condition.
} else {
// Get All Data
}
}
#GetMapping("/search")
public List<Entity> getSearchResult(#RequestParam("parameter_name") Optinal<String> variable) {
if(variable.isPresent()) {
// Get Data based on condition.
} else {
// Get All Data
}
}
I have an application where there will be several parameters passed to my endpoint for searching, these parameters are not defined because they are dynamically generated so i cannot map it to a specific model. What would be the best way to map any query parameters into my GET endpoint?
[HttpGet]
public CustomResponse GetResults({something here that will map the parameters?})
{
//perhaps a dictionary? a collection of some sort?
}
Then I need to get all those keys and values and search the database for anything containing that and as i said it could be anything.
So I could pass something like?
/api/Merchandise/GetResults?sku=30021&cupsize=medium&color=red&location=south& {and all the dynamic fields which could be anything}
HttpRequest object has Query property that is an IQueryCollection and holds all passed query parameters.
In other words, in your action method you may do:
[HttpGet]
public CustomResponse GetResults()
{
var queryParams = HttpContext.Request.Query;
// directly get by name
var value1 = queryParams["parameter_name"];
// or queryParams.TryGetValue()
foreach (var parameter in queryParams)
{
string name = parameter.Key;
object value = parameter.Value;
}
}
You could map it to JObject, which is like a Dictionary.
Don't forget:
using Newtonsoft.Json;
I have controller functions that look like:
public function get(Request $request){
return json_encode($this->users->get($request->all));
}
where $this->users references a repository for my User model. Inside of this repository, I have a function that looks like:
public function get($request){
return User::where($request)->get()->toArray();
}
I can dynamically pass in any number of parameters to search on through my HTTP request with the above code without having to explicitly state the column names.
This works well as long as all the parameters passed in through the request are valid column names. It will error out if I pass in a parameter that is a not valid table column.
I have the protected $fillable array defined in each of my models corresponding to my repositories.
Is there anyway to enforce what can be passed into the where() method above so that only valid columns defined in the $fillable array in my model are ever passed in?
For example, let's say I have a users table with columns (id, name, description). The GET URL I pass in looks like:
GET /user?name=mike&description=skinny&age=45
There are three parameters in the URL above, but only two of them are valid column names. I would like the age parameter to be automatically taken out. In my current code, the above URL will return an error response since the age column does not exist.
You can use array_only() helper:
return User::where(array_only($request, $this->fillable))->get()->toArray();
It will filter $request and will keep only key/value pairs which are in the $fillable array.
If this code is not in a model, change $this->fillable to appropriate variable or method call.
I wrote a package for doing this kind of filtering for models. It will allow you to filter based on User::filter($request->all())->get(); while defining each possible column's where() constraint.
https://github.com/Tucker-Eric/EloquentFilter
Try doing something like this:
public function get(Request $request)
{
$user = new User();
return User::where(
$request->only($user->getFillable())
)->get()->toArray();
}
By creating an instance of User you can then access it's fillable properties using getFillable(). By passing this list into $request->only(...) you can restrict the values you pull out to only those that are in your fillable array. Hopefully that should mean only fillable values are considered as part of your query.
Hope it helps.
I have a situation where starting from the 2nd call and subsequent ones (Ajax GET calls), AutoMapper is reusing the previous value (the value from the 1st call that comes from a click in an action link). It's like a "caching" problem...
public virtual ActionResult List(int assessmentId, int? chapterId, bool? isMenuClick)
{
Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => assessmentId));
...
}
It doesn't matter if I use UseValue, ResolveUsing or MapFrom in the above opt => lambda. The behavior is the same, that is, it reuses the value from previous calls.
AssessmentId property does not exist in the source type ( Element ). This way I try to assign AssessmentId a value that "may" change dynamically during subsequent calls to the method where I have this code. assessmentId is a parameter in my ASP.NET MVC action method as shown above in the method signature.
Then I call this code in the List action method:
var questions =
Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
(Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
e.Standard.ChapterId == chapterId));
The first time, questions is OK, that is, all AssessmentQuestionViewModel objects have the AssessmentId property set correctly as per the CreateMap defined.
Starting from the 2nd call, it reuses the assessmentId from the 1st call and it messes up with my business logic because I expect it to map AssessmentId to the updated assessmentId that's being passed as a parameter to the List method.
Just to be sure: I've set a breakpoint in the code and I can see that the value of the assessmentId parameter is correct. It's just the returned mapped objects questions that have the wrong value in the AssessmentId property - a value that differs from the current assessmentId value. The values should be equal as I understand it since I'm asking AutoMapper to do the mapping using that current value.
I have AutoMapper 2.2.1-ci9000 (Prerelease), but I tested this with the previous version and I saw this same behavior. I updated to the Prerelease thinking that this "misbehavior" would go away.
I think this is a bug. Please correct me if I'm wrong or if I'm trying to use it in a way not supported. :)
I think the problem here your trying to create multiple mappings of the same type - which AutoMapper doesn't support. Everytime your List action is called, you create a new mapping (which has a different ForMember(...) clause). AutoMapper won't throw an exception it just ignores the duplicate mapping so what you are seeing here isn't a bug, it's expected behaviour.
ForMember is infact called on every map, however, you have a scoping issue here as your variable is hard-coded into the expression. As a work-around you could do something like:
public class MyController
{
public MyController()
{
// define mapping once, but make assessment expression dynamic
Mapper.CreateMap<Element, AssessmentQuestionViewModel>().
ForMember(dest => dest.AssessmentId, opt => opt.MapFrom(e => GetCurrentAssessmentId()));
}
private int GetCurrentAssessmentId()
{
return (int)TempData["AssessmentId"];
}
public ActionResult List(int assessmentId, ...)
{
// store current assessment temporarily
TempData.Add("AssessmentId", assessmentId);
// execute mapping
var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>
(Database.Elements.Where(e => !elementIds.Contains(e.ElementId) &&
e.Standard.ChapterId == chapterId));
}
}
I will say though, your jumping through a lot of hoops for this to work, it would be much simpler to manually set the property without the help of AutoMapper e.g.
var questions = Mapper.Map<IEnumerable<Element>, IEnumerable<AssessmentQuestionViewModel>>(...);
foreach (var q in questions)
{
q.AssessmentId = assessmentId;
}
When passing a RouteValueDicitonary or anonymous object into a #Url.Action method (or any of its analogs), is there a way to properly pass in a collection object or IEnumerable so that it would generate a url that would be compatible with the default model binder?
For example, say I have an action like this:
public ActionResult Index(ICollection<int> ids)
{
...do something
}
and in my template I do something like this:
#Url.Action("Index", routeValues:new int[]{1,2,3})
the goal is to have a url output like this:
... /index?ids=1&ids=2&ids=3
but the url output is actually something like this:
... /index?ids=System.Int[]
I'm assuming there is no support for this currently. If not, then at what part in MVC do I need to create a custom handler or whatever to override this default functionality?
Unfortunately currently there is no existing helper that will allow you to generate such url. So one possibility is to do it manually:
#(Url.Action("Index") + "?" + string.Join("&", new int[] { 1, 2, 3 }.Select(x => "ids=" + x)))
or write an extension method to encapsulate the logic:
#Url.ActionWithIds("Index", new int[] { 1, 2, 3 })
Since those are integers we don't need url encoding but if you want to do the same thing with a collection of strings the values should be properly url encoded.
Pass each element of the array to a RouteValueDictionary using the model binding syntax for round trip purposes.
int i = 0;
var temprvd = arr.ToDictionary(m => String.Format("name[{0}]", i++), m => (object) m);
#Url.Action("index", new RouteValueDictionary(temprvd));
or concat on to an existing route value dictionary.
Cast the value to an object (as shown above) to ensure the correct RouteValueDictionary constructor overload is used.
For use the default model binder, you should end up with something like :
?ids=1&ids=2&ids=3
you can returns a private collection named HttpValueCollection even the documentation says it's a NameValueCollection using the ParseQueryString utility.
Then add the keys manually, HttpValueCollection do the encoding for you.
And then just append the QueryString manually :
var qs = HttpUtility.ParseQueryString("");
new List<int> { 1, 2, 3, 4, 5 }.ForEach(x => qs.Add("ids", x.ToString()));
#Url.Action("Index")?#qs