Laravel-datatables protection - javascript

I'm using https://github.com/yajra/laravel-datatables currently to send ajax request for my datatable but noticed this:
columns: [
{ data: 'first_name' },
{ data: 'last_name' },
{ data: 'email' },
]
but those are lines in my javascript document. I'm currently providing data like this:
return Datatables::of(User::all())->make(true);
That means if someone change lines in my js file he can view columns that he is not supposed to. Now if you take a look at github documentation you can see that this package can also accept collection or query. Is it possible to target only certain columns from database with collection or query ?

You can fetch only necessary columns from database like this:
return Datatables::of(User::all(['first_name','last_name','email']))->make(true);

Related

Multiple tables with individual search inputs in serverSide mode using DataTables

Application is using:
DataTables 1.10.18
jquery 3.2.1
PHP back-end
lodash 4.17.4
The application contains a web page which consists of multiple DataTables. Each of these uses serverSide: true (server-side mode) to obtain the data via an ajax endpoint which returns JSON data.
The tables are initialised as follows:
On page load several <table>'s are rendered. I'm using a jquery .each() to initialise the DataTable for each one:
$.each($('table'), function () {
$(this).DataTable({
processing: true,
serverSide: true,
searching: false,
ajax: {
data: {
table_id: $(this).attr('id')
},
url: '/get-data.json',
},
...
});
Each <table> has an ID. This is passed via ajax in the data: attribute. The endpoint /get-data.json returns data based on the table ID. In other words it knows "which table" the data should be obtained for based on this ID.
I want to be able to do searching on tables, but it must be done server-side. For this reason my initialisation code in (1) sets searching: false because this effectively disables the client-side search facility that DataTables provides (which we can't use in this instance as searching must be done server-side).
The problem I'm facing is how to create search inputs for each table, make an ajax call and update the appropriate table. I want the search to work in realtime after >=3 characters have been entered. Critical to this question is that 1 search input is responsible for searching 1 DataTable - it's not a search feature where the input can update "any/every table on the page" which is a commonly described pattern in other questions. 1 input : searching 1 table in this case.
My plan has been as follows - each table referenced in point (2) has an ID. I need to create unique inputs. So if I have tables with ID's #table1, #table2, #table3 I can easily create:
<input type="text" name="table1_search" id="table1_search">
<input type="text" name="table2_search" id="table2_search">
<input type="text" name="table3_search" id="table3_search">
I then detect if any changes have occurred on inputs:
$('input[type="text"]').bind("keyup change input",
function (e) {
// Ignore tab key for keyup event otherwise it'll fire an ajax request that does nothing useful.
if (e.which !== 9) {
processSearch.call(this);
} else {
e.preventDefault();
}
});
var prev_value = {};
function processSearch() {
var obj = $(this),
search_id = obj.attr('id'), // ID of input
search_value = obj.val(); // Value of input
// There's been no change to the field, ignore.
if (prev_value[search_id] === search_value) {
return;
}
prev_value[search_id] = search_value;
/* Wait until at least 3 characters have been entered, or user has cleared the input */
if (search_value.length >= 3 || (!search_value)) {
debouncedDraw({search_id: search_id, search_value: search_value});
}
}
The above code does what I need in terms of waiting for >=3 characters to be entered. I'm then executing a function called debouncedDraw which passes an object containing search_id and search_value. These refer to the input ID and value respectively, e.g. if I type "foo" into #table1_search then the object is:
{search_id: 'table1_search', search_value: 'foo'}
The debouncedDraw function looks like this. This is using lodash to limit the rate at which the function can fire. The point here is to stop it making needless ajax requests based on a question I asked a few years ago here: DataTables - kill ajax requests when a new one has started:
var debouncedDraw = _.debounce(function (opts) {
console.log(opts);
}, 500);
At the moment this will just console.log the object given above.
I'm unsure of the best way to proceed at this point. I need to re-run /get-data.json via ajax and then update the appropriate table.
I could access the request data and split the search_id based on the underscore to work out which table ID the data is for (e.g. table1_search targets #table1). I then need to write this data back to the appropriate table (#table1 in this case).
I can't help but think I'm going about this in a convoluted way and wondered if DataTables itself has any better ways of supporting this? It seems quite a basic requirement (multiple searchable tables in serverSide mode). But I can't find any posts which refer how to do this specifically.
All the "gotchas" I've experienced over the years is encapsulated in the snippet below. This is the basic template I always use when creating a new datatable. You can create as many datatables on a page as you need using this pattern.
Personally I would use a different ajax url path/route for each table so that table logic is in separate files in the backend... but it is possible to have all the data logic in a single backend file. I modified my usual template to suit that.
<script> //I usually put the script section in the head tag
var table_1; //declare your table var here and initialize as a datatable inside document ready below.
$(document).ready(function() {
table_1 = $('#table_1').DataTable( {
dom: "Bfrtip",
ajax: {
url: "/get-data.json?table=table_1", //add query string var for backend routing
type: "POST" //use POST to not have to deal with url encoding various characters
},
serverSide: true,
searchDelay: 2000, // use this instead of custom debounce
processing: true, // optional visual indicator that a search has been sent to backend
lengthMenu: [ 10, 25, 50, 75, 100 ], // define per page limits. first value will be the default
buttons: [
"pageLength" // per page drop down button. i usually override/extend the default button
],
columns: [ // column definitions of json data fields
{ data: "col_1", title: "ID", width: "1%" }, // width: 1% makes col width as small as possible
{ data: "col_2", title: "Label 2", visible:false }, //visible: false allows you access to field data without displaying to user
{ data: "col_3", title: "Label 3", render: function ( data, type, row ) { //render allows combining of fields into single column
return data + ' <small>('+row.col_2+')</small>'; // data will be col_3 value. row.col_2 is how you reference col_2 value
} },
{ data: "col_4", title: "Label 4", searchable:false }, //searchable: false set this field to not be used in search
],
rowId: 'col_1' //sets the tr row id to the value in this column. useful for DOM and other manipulation later
} );
}
</script>
<table id="table_1" class="table table-striped table-bordered table-sm" style="width:100%"></table>
<!-- If you define title attributes in col definitions above you don't need to create html table headers/footers. Just an empty table tag will do. -->
With this pattern you can utilize the built-in search input that comes with datatables for your use case with server-side processing on all tables.
There's a method behind my madness which I tried to document in the script comments on each line. Let me know if you have a question on something. I'm thinking this is bounty worthy.
For reference, when developing a new app using datatables I basically live on this page https://datatables.net/reference/option/
Edit 1
Inside your existing debounced drawTable function you could do something like this:
function drawTable(id) {
$('#'+id).DataTable().ajax.url( 'get-data.json?table_id='+id+'&foo=bar' ); //update ajax url of existing dt - if necessary
$('#'+id).DataTable().search(search_input_val).draw(); // fire ajax request with value from your custom search input
}
I'm fairly certain you will need to set "searching" to true though for this method to work.
Edit 2
Another way I just thought of, without using dt search. Pass all your data through modified url and load/reload.
$('#'+id).DataTable().ajax.url( 'get-data.json?table_id='+id+'&search=foo' ).load();
You could then get rid of all the debounce stuff if you use a button click listener or an onblur listener on the input field and fire the same command above.
Have you seen this? https://datatables.net/reference/api/%24.fn.dataTable.util.throttle()
I've never used it before, but it looks like a debounce. The example on the page shows it being used for .search()
I've implemented the following but would prefer a better solution as I don't think this is efficient and it definitely isn't elegant!
Taking the code from the question I modified the debounce function as follows:
var debouncedDraw = _.debounce(function (opts) {
// Destroy the existing DataTable.
$('#' + opts.search_region).DataTable().destroy();
// Re-run the drawTable method to get the new DataTable with the search results
drawTable(opts.search_region);
}, 500);
I introduced a function called drawTable which takes the ID of a <table> and runs the DataTables initialisation code. The ajax object was also modified to take into account anything entered into the search keywords input for the given table ID:
function drawTable(id) {
$id = $('#'+id); // Convert string ID to jquery identifier
$id.DataTable({
// DataTable initialisation code as per question
ajax: {
data: {
table_id: id,
keywords: $('input[name="keywords_' + id + '"]').val() // search keywords for table_id
},
url: '/get-data.json',
},
// ... code as per question
});
}
The $.each() was modified so that it detects the ID of each <table> on page load and calls drawTable:
$.each($('table'), function () {
drawTable($(this).attr('id'));
});
This "works" in that it creates each of the required DataTable's on page load, and also handles the search. The search input names were modified to the format: keywords_ plus the ID of the table, e.g. keywords_table1.
I don't think this is efficient because I'm having to call destroy on my DataTable. As per the docs:
This has a very significant performance hit on the page, since a lot of calculations and DOM manipulation is involved, so if you can avoid this, and use the API, that is very strongly encouraged!
However the reason I'm doing this is also as given in the same docs:
DataTables does not allow initialisation options to be altered at any time other than at initialisation time. Any manipulation of the table after initialisation must be done through the API
Well, I'm not using the client-side search feature as I'm having to do searching server-side. So I'm unsure whether manipulating the table via the API would actually help in this instance anyway.
Are there better ways of achieving this?

Adonis.js populate all with related tables data

I use MySql, all relationships have been configured correctly in models and schemas:
I have 3 tables:
1. posts ( main )
2. categories ( 1 post has 1 cat, 1 cat belongs to many posts )
3. tags ( many-to-many )
Categories and tags both have "post_id" column relating to "id" column in posts table.
What is the best way to get all related data ( post data + post category data + post tags data ) ?
( for now I know I can get something like:
const post = await Post.find(params.id)
const category = await post.categories().fetch()
etc for every related table
I'm sure there must be better way.
I'd like result to be:
{
post data - name, text ...,
"category": {
category data - name, text ...
},
"tags": [
{
tag1 data
},
{
tag2 data
}
]
}
const wantedData = await Post.query().where('id', params.id).with('categories').with('tags').fetch()
Yes, there is a better way to do this.
The Latest Adonis js Have the feature of the Query builder. You can perform almost all SQL queries using this. For Example
#select
Database
.from('users')
.select('id', 'username', 'email')
So There are many queries you can perform as your need. The following links will be very useful for you. Query builder Documentation

Auto-complete or type-ahead search box using the semantic-ui framework and an ajax request

I'm using the semantic-ui framework to create a simple search form that uses auto-complete information generated by calling an api via an ajax request.
The server endpoint generates a simple JSON array
e.g.
http://data.nzor.org.nz/names/lookups?query=lu
gives
["Lubbockia","Lubbockia aculeata","Lubbockia squillimana","Lucanidae"]
I can see the search making the request but I'm not sure how to get the results to display.
I have created a jsfiddle at http://jsfiddle.net/6ojkdvnn/4/
$(document)
.ready(function () {
$('.ui.search')
.search({
apiSettings: {
url: 'http://data.nzor.org.nz/names/lookups?query={query}'
},
debug: true,
verbose: true
});
});
I have tried various options but have now stripped it back to the basic settings above so as not to confuse the matter. The documentation is pretty good (http://semantic-ui.com/modules/search.html) but I can't quite see how to make it work.
I'd prefer not change the api response if it can be helped.
I too had problem with the seach api of the Semantic-UI.
So after some researchs, i learned that it can be used this way:
I am using Ruby on Rails also.
jQuery File to autocomplete cities names:
# Semantic-Ui Search
# Sets for autocomplete name of cities while typing.
$('.ui.search.city').search
apiSettings:
action: 'search', url: '/cities/autocomplete.json?query={query}'
fields:
results : 'cities'
title : 'name'
description : 'state'
minCharacters : 3
Semantic-UI (search) expects a "results" as root and nodes with childs content with title and description, and others that api specify. So if you had json result with other names, then you have to change in the method call of searc function.
Because of this, i changed results for cities, title for name and description to state.
In my Controller i just made a query like this:
def autocomplete
#cities = City.includes(:state).order(:name).where('name like ?', "%#{params[:query]}%")
end
In my Routes file i specify the get method that return a collection.
# Resources for cities.
resources :cities, only: :index do
get :autocomplete, :on => :collection
end
And using the Rabl gem i format the output of json file:
collection #cities, root: :cities , object_root: false
attributes :id, :name
node(:state) { |city| city.state.name }
node(:query) { params[:query] }
Thats it's all, it works for me.
Now, for query http://data.nzor.org.nz/names/lookups?query=lu, server responds with XML data. It is not JSON.
<ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<string>Lubbockia</string>
<string>Lubbockia aculeata</string>
<string>Lubbockia squillimana</string>
<string>Lucanidae</string>
<string>Lucaninae</string>
<string>Lucerapex</string>
<string>Lucerapex angustatus</string>
<string>Lucerne</string>
<string>Lucerne Australian latent virus</string>
<string>Lucerne dodder</string>
</ArrayOfString>
In search module code, semantic require both response.success and response.results object in response
For example, line 1050, if(response.results !== undefined) {
In API description, it is not clear if you can modifiy the response before uses by Semantic. May be use the callback onSuccess: function() {
described at http://semantic-ui.com/behaviors/api.html#/usage
But I am sceptical...

Extjs 3.4 custom JSONReader

I haven't had a question from quite a while, but I finally ended up hitting a wall here. Anyways, to make this easy. I am trying to create a JSON store in extjs 3.4 (work related, and no I am not updating).
Well, I created the queries as usual with the proper response and fill it up. My problem is that I have 1 extra property on the JSON that I want to be able to pull and use on the app.
Sample of my JSON from the response object in chrome:
myInventory: [{Priority:1, PMNumber:444, Description:fix-a-tape, Assets:3, Storage_Count:0,…},…]
percent: 97.040498442368
totalCount: "3"
Now, I know this is correctly formatted because the Grid I am using gets populated, but I can't get the percent property. So my question is, how do you pull an extra parameter on the datastore building block of code when you have one extra parameter that is not usual on EXTjs, in my case the percent?
I tried doing a metachange on the JSONReader, but all I get is percent:"percent" on the properties as I inspect the datastore after it's creation.
Ext.data.JsonReader has a property jsonData which is exactly what you need. A quick example:
Ext.onReady(function() {
var store = new Ext.data.JsonStore({
url: 'data/test.json',
root: 'dataRoot',
fields: ['fieldA', 'fieldB'],
idProperty: 'id',
storeId: 'myStore'
});
store.on('load', function(store) {
console.log(store.reader.jsonData.someOtherProperty);
});
store.load();
});
and the data/test.json looks like this:
{
dataRoot: [{
id: 0,
fieldA: 'a',
fieldB: 'b'
}],
someOtherProperty: 'abc'
}
There could also be an alternative approach that you manually (not via store.load()) perform Ext.Ajax request, use the properties you need and then manually call Ext.data.JsonStore.loadData() using the data you got from Ajax request.

ExtJS 4.1 - Retrieve hasOne information for Nested JSON

I am attempting to retrieve the information for a Model and an associated model that contains a hasOne association. I was referencing the following Sencha documentation page (http://docs.sencha.com/ext-js/4-1/#!/guide/data). I currently have the following sample code working:
var Mtd = Ext.ModelMgr.getModel('Mtd');
Mtd.load(4, {
success: function(mtd){
console.log("Loaded! " + mtd.get('id'));
mtd.getTreatmentdesign(function(treatment,operation){
console.log(treatment.get('id'));
}, this);
}
});
Now, when I call mtd.getTreatmentdesign(), I notice that two requests are made to retrieve information. The first one is to retrieve the Mtd information which I am expecting but then it's also making a request to retrieve the Treatmentdesign information. The response for the Mtd contains the Mtd information as well as the Treatmentdesign information. So I want to process the Mtd and Treatmentdesign information with one request. It puzzled me that the documentation stated the following:
You may be wondering why we passed a success function to the User.load call but didn't have to do so when accessing the User's posts and comments. This is because the above example assumes that when we make a request to get a user the server returns the user data in addition to all of its nested Posts and Comments. By setting up associations as we did above, the framework can automatically parse out nested data in a single request.
So how can I retrieve associated information without having to make another request? I simply just want to use all the json from a single request as opposed to having to make multiple requests.
Be sure to set the associationKey config on the HasOne association to the property that contains the data for the associated model. By default, this is the name of the associated model class in all lowercase letters.
For instance, if the data for an Mtd record is returned by the server in the form
{
...
treatmentDesign: {
...
}
}
set the associationKey to 'treatmentDesign'.
Here's an example in action: http://jsfiddle.net/HP6fq/3/
Yes, associationKey works
Ext.define('User', {
extend:'Ext.data.Model',
fields: ['id', 'name', 'status'],
associations: [{ type: 'hasOne', model: 'Status', associationKey: 'status' }]
});
Ext.define('Status', {
extend:'Ext.data.Model',
fields: ['id', 'title'],
});
Demo here

Categories

Resources