Pagination on ruby array without sending request to controller - arrays

I have a array of arrays in my rails application and it is something like:
#array=[[1, "Mr.X", "developer"], [2, "Mr.Y", "python"]...... [n, "Mr.Z", "ba"]]
I am getting these records on runtime from a different database server by using SQL query not from ActiveRecord. I want to perform pagination on this data and I am using will_paginate/array
#array = #array.paginate(:page => 1, :per_page => 1)
and in my view, I am calling it by:
<%= will_paginate #array%>
It is showing me the data of one record for the first page and links for other pages, however when clicked on next page link, it is giving the error for nil:nil class, as it is again submitting the parameters but this time, the parameters are not appropriate.
I have the final records in #array and I don't want to send the request in backend again and again and I just want to process it on front end only.
Thanks in advance.
Note: I am having different issue, while submitting the form on the post method for first time, my params are
params = <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"EEzvZTAdFAGZ1vOvfeQ/hx7BmDFknC+xHpP4HdhIUncfZDobL1le1vzeanLRdgVFQtKYLGPCgKOfqf5J9+4J2Q==", "check_box_array"=>["ID", "NAME", "ROLE"], "column_value"=>{"ID"=>"", "NAME"=>"", "ROLE"=>""}, "table_name"=>"employee", "column_length"=>"3", "commit"=>"Search", "controller"=>"test", "action"=>"index"}
and here on the controller index action I am running SQL query and fetching data from database not using any model and storing the results into array, which I am passing to view like ` <%= will_paginate #array %>
But while clicking on any page number, it is submitting the parameters as <ActionController::Parameters {"page"=>"2", "controller"=>"test", "action"=>"index"} permitted: false>
and this time, it is not having any query parameters and it is generating the error for nil class
undefined method for nil:NilClass
But I don't want to send the query parameters, I just want to show the data of #array using pagination i.e. on every page just the required data.

Related

Cakephp 3 - How to integrate external sources in table?

I working on an application that has its own database and gets user information from another serivce (an LDAP is this case, through an API package).
Say I have a tables called Articles, with a column user_id. There is no Users table, instead a user or set of users is retrieved through the external API:
$user = LDAPConnector::getUser($user_id);
$users = LDAPConnector::getUsers([1, 2, 5, 6]);
Of course I want retrieving data from inside a controller to be as simple as possible, ideally still with something like:
$articles = $this->Articles->find()->contain('Users');
foreach ($articles as $article) {
echo $article->user->getFullname();
}
I'm not sure how to approach this.
Where should I place the code in the table object to allow integration with the external API?
And as a bonus question: How to minimise the number of LDAP queries when filling the Entities?
i.e. it seems to be a lot faster by first retrieving the relevant users with a single ->getUsers() and placing them later, even though iterating over the articles and using multiple ->getUser() might be simpler.
The most simple solution would be to use a result formatter to fetch and inject the external data.
The more sophisticated solution would a custom association, and a custom association loader, but given how database-centric associations are, you'd probably also have to come up with a table and possibly a query implementation that handles your LDAP datasource. While it would be rather simple to move this into a custom association, containing the association will look up a matching table, cause the schema to be inspected, etc.
So I'll stick with providing an example for the first option. A result formatter would be pretty simple, something like this:
$this->Articles
->find()
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$userIds = array_unique($results->extract('user_id')->toArray());
$users = LDAPConnector::getUsers($userIds);
$usersMap = collection($users)->indexBy('id')->toArray();
return $results
->map(function ($article) use ($usersMap) {
if (isset($usersMap[$article['user_id']])) {
$article['user'] = $usersMap[$article['user_id']];
}
return $article;
});
});
The example makes the assumption that the data returned from LDAPConnector::getUsers() is a collection of associative arrays, with an id key that matches the user id. You'd have to adapt this accordingly, depending on what exactly LDAPConnector::getUsers() returns.
That aside, the example should be rather self-explanatory, first obtain a unique list of users IDs found in the queried articles, obtain the LDAP users using those IDs, then inject the users into the articles.
If you wanted to have entities in your results, then create entities from the user data, for example like this:
$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);
For better reusability, put the formatter in a custom finder. In your ArticlesTable class:
public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
return $query->formatResults(/* ... */);
}
Then you can just do $this->Articles->find('withUsers'), just as simple as containing.
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods

Apollo Client React container add created element to paginated list of elements without refetching

This title is quite confusing, but I will try to explain the best that I can. I have a situation where I am collecting and displaying a list of data elements from the server in React Native. It uses a query that sends a GraphQL parameter to the server to limit the initial request to the first 15 elements.
query GetElements ($count: Int) {
elements (count: $count) {
id
name
tye
}
}
As previously stated, this will query the main list with an initial count of 15 elements.
I have another page of the app that a user can create a new element on the backend using a different GraphQL query.
My question is: Is there a way to easily update the main list of elements to include the newly created element without performing any addition network requests? In other words, I want to be able to simply add the new element to the main list of elements whenever the create command is successful.
The problem is: Apollo's readQuery command on the update proxy will not return to me the appropriate data set to update unless I know the variables that were send with the initial query; this includes the pagination count variable.
You can now define the key for the queries cache store like this:
query AllItems($cursor: String, $limit: Int, $query: String) {
items(cursor: $cursor, limit: $limit, query: $query) #connection(key: "AllItemsQuery") {
count
cursor
has_next
has_prior
data{
...CoreItem
}
}
}
if you would just like to receive the added item, it seems like you want subscriptions :: http://dev.apollodata.com/react/subscriptions.html

Agile Toolkit - OR statement in combination with Model

I have a Model from which I want to select all rows for which 'caller' or 'callee' is a given value for presentation in a Grid.
I have tried lots of different avenues of accomplishing this and cannot get anywhere with it.
I have a working filter on the Grid which narrows down the results by date (starting and ending dates), by status ("ANSWERED","NO_ANSWER"), I can also add conditions for 'caller' and 'callee', but how do I get it to show all rows where either 'caller' or 'callee' is a match to the current $UserID? Basically showing all calls (rows) a user was involved in?
The MySQL query itself is a simple OR construction, but how do I 'feed' it into the Model or the Grid so that it plays nicely with the other filters on the page?
You can use orExpr() method of DSQL to generate SQL for your needs.
For example,
$m = $this->model->debug(); // enable debug mode
$user_id = $this->api->auth->model->id;// currently logged in user ID
$q = $m->_dsql(); // get models DSQL
$or = $q->orExpr(); // initialize OR DSQL object
$or->where('caller', $user_id) // where statements will be joined with OR
->where('callee', $user_id);
// use one of these below. see which one works better for you
$q->where($or); // add condition with OR statements
$q->having($or); // add condition with OR statements
Of course you can write all of this shorter:
$m = $this->model->debug(); // enable debug mode
$user_id = $this->api->auth->model->id;// currently logged in user ID
$q = $m->_dsql(); // get models DSQL
$q->where($q->orExpr()
->where('caller', $user_id)
->where('callee', $user_id)
);

Backbone - fetched model, set an attribute(modify), then save model, it should update but sending POST request

I created my web site having 2 types of users: admin and user. So, I created 3 pages mainpag.html, admin.html, user.html. and separate models, views, collections, routers.js files for each of them. After logging in, as I am sending users to separate HTML pages with different models, I can't automatically get user model. so I did like this:
First, I made AJAX call to server, asking for the _id (username in session, so I can get id)
from the id, I fetched the model, by model.fetch(), then I got my usermodel with all attributes.
then in the success callback of fetch, I did model.save({weight: "somevalue"}). According to me, it should update right, as the model is already available, that attribute weight also available with some old value, but it is sending POST request, also when I tried model.isNew(), it returned true. Where am I wrong? how can I update my model? I will post more details if required.
More details:
If I remove that save method, then I am getting correct attributes in the model.
If I don't remove that save method, that success and error callbacks are also appearing as attributes in the model.
Code:
addWeight : (e)->
arr=new Array()
arr['_id']=app._id
console.log "asdasd"
console.log arr
console.log arr['_id']
#user_model =new UserModel(arr)
#user_model.fetch({
success : (model,res,options) =>
console.log model
console.log res
arr=new Array()
arr['_id']=e.target.id
#arr['action']='weight' #means , update weight
##user_model.setArr(arr)
##user_model.set({weight : arr['_id']})
console.log "new : "+#user_model.isNew()
#user_model.save({weight : e.target.id})
##user_model.save({
# success : (model,res,options) =>
# console.log "model updated: "+JSON.stringify(model)
# console.log "Res : "+JSON.stringify(res)
# error : (model,res,options) =>
# console.log "Error : "+JSON.stringify(res)
#})
error : (model,res,options) =>
console.log "Error "
})
the above code is written in coffeescript, so even if you don't know coffeescript, don't worry, you can understand easily, and those # mean, it is a comment. here we follow indentation instead of braces.
one more doubt, a model's URL must be changed dynamically according to the requirement, right? what is the best way to achieve that? I am doing like this:
I am populating "array" containing the required fields that should be present in the URL. In model, s init func, I am using #arr=arr, then in URLs function, I check like this.
url : ->
if #arr['id']
"/user/#{#id}"
Is my approach right, or any better approach is there for dynamically setting URLs. Or can I directly set the URLs like this:
#user_model.setUrl "/someurl/someid" //this setUrl method is available in model's definition
#user_model.fetch() or save() or watever that needs url
Just a hunch, but you mentioned that you call model.fetch() to retrieve the _id field. Be sure to either return an id field instead _id (notice the underscore).
The call to model.isNew() returning true is an indicator that the id property was never set from the model.fetch() call.
I look forward to a possible further explanation with your code...
Looking at your code:
/* The model needs an 'id' attribute in order to marked as not new */
#user_model = new UserModel(id: arr['_id'])
Actually if you call
model.set({weight: "somevalue"});
It will update the value in the model, but it won't send a POST request
model.save(attribute);
Actually calls Backbone.sync as you probably know.
EDIT :
You might want ot set
m = Backbone.Model.extend({
idAttribute: '_id'
});
to every model, because the isNew method actually checks if the model has id attribute
Regarding to this you could see here that .set doesn't call backbone.sync here : http://jsfiddle.net/5M9HH/1/

How do I send arrays between views in CakePHP

I am not sure if I formulated the question right, but still ...
I have a view that shows a flash embed and this flash take as parameter a /controller/action URL that generates a XML. I nee to send, from this view, an array to the XML generator action. How is the best way ? Is there some helper->set() method like or I have to create an specific URL to send this array to that action ?
Here goes my structure:
my_controller.php
function player() {}
player.ctp
<div id="myDiv">Here it Goes</div>
<script type="text/javascript">
var so = new SWFObject('player.swf','test','50','50','8');
so.addVariable('file','/xml/generate'); // need to pass an array here
so.write('myDiv');
</script>
xml_controller.php
public function generate() {
// I need to read an array here
}
generate.ctp
echo "<xml><data>" . $array['contents'] . "</data>";
If the array is small enough, serialize then urlencode it and add it as a paramter to the url to your generate action:
player.ctp
so.addVariable('file','/xml/generate/<?php echo urlencode(serialize($array)); ?>');
then read it back:
public function generate($array) {
$array = unserialize($array);
}
Save the array in the session then in the next request to the XML generator action, read it back from the session.
my_controller.php
function player() {
$this->Session->write('key', $array);
}
xml_controller.php
public function generate() {
$array = $this->Session->read('key');
}
However, I have heard of some problems where flash sometimes doesn't send session cookies, in which case, append the session id to the url of the action:
so.addVariable('file','/xml/generate/<?php echo $session->id(); ?>');
and to get the session back:
public function generate($sessionId) {
CakeSession::id($sessionId);
$array = $this->Session->read('key');
}
First of all you cannot send data from one view to another in the manner you are speaking. Each of those calls would be a separate request and this means that it goes out of the framework and then in again. This means that the framework will be built and tear down between calls, making impossible to pass the data between views.
Now in regards to the array that has to be sent to your action, I'm utterly confused. I don't think you are looking at the problem the right way. If that action needs an array of data and then produce XML so the Flash Object can get it, then it makes even less sense. Are you sure that the Flash Object isn't the one responsible to sending that array of data to the Param you mentioned?
Well, even if all you are saying has to be done quite like that, I'll suggest you drop that array on the file system and then pick it up when the action is called by the Flash.
Or another suggestion would be to use AJAX to send that array to the action.
Both suggestions imply my utter "cluelessness" on your predicate.
I still have to ask, isn't the Flash Object gonna do something in all this?
You can send an array with data from a view to a controller in CakePHP like this.
To the link you can pass arguments:
www.site.com/model/action/param1:foo/param2:test
You can then retrieve them in the controller action in the following way:
$yourarray = $this->params['named'];
Of course the array shouldn't be too large then.

Resources