Backbone JS + Codeigniter Restful API and MYSQL Substring function not working - php

I have this BackboneJS Application where I use Codeigniter as Backend. I use the RESTful API to return data from my MySQL Database to the Frontend. As part of my application, I want to display Music artists Albums and the relevant tracks and the tracks duration. Right now, I get the track duration like 00:03:26 but I want it to display 03:26, so I made this MySQL-query where i use the SUBSTRING-method:
public function artist_tracks_get($artist_id)
{
$this->load->database();
$sql = "SELECT products.title AS album_title, products.genre AS album_genre, products.ccws_pro_id AS product_upc, track_title, SUBSTRING(track_duration, 4) AS track_duration FROM products
INNER JOIN track_albumn_information ON products.ccws_pro_upc = track_albumn_information.product_upc
AND track_albumn_information.artist_id = '".$artist_id."' LIMIT 0 , 30";
$query = $this->db->query($sql);
$data = $query->result();
if($data) {
$this->response($data, 200);
} else {
$this->response(array('error' => 'Couldn\'t find any artist albums!'), 404);
}
}
And inside my Backbone View I have:
serialize: function() {
var grouped = _.groupBy(this.collection.toJSON(), function(item) {
return item.album_title;
}), spanned = [];
_.each(grouped, function(item) {
var firstItem = item[0],
spannedItem = {
'album_genre': firstItem.album_genre,
'album_id': firstItem.album_id,
'album_title': firstItem.album_title,
'cover_image': firstItem.cover_image,
'digitalReleaseDate': firstItem.digitalReleaseDate,
'physicalReleaseDate': firstItem.physicalReleaseDate,
'pro_id': firstItem.pro_id,
'product_upc': firstItem.product_upc,
'tracks': []
};
_.each(item, function(albumItem) {
spannedItem.tracks.push({
'track_duration': albumItem.track_duration,
'track_title': albumItem.track_title
})
})
spanned.push(spannedItem);
});
return spanned;
}
When I run this query on my local phpmyadmin, I get the right result like 03:26 but when I test it online on my webserver, I get 00:03:26... What is the issue here? Is it the SQL version? Is BackboneJs unable to handle the SUBSTRING-method?
Please help! Thanks in advance...

Backbone has no idea what you are doing in MySQL so it's not Backbone having a problem with MySQL SUBSTRING().
If you wanted to avoid dealing with MYSQL, you could simply do
albumItem.track_duration.substr(3);
in the view to display it as "03:26".

Related

Prevent Multiple users executing query same time and redirecting to different sessions php/mysql

I have created schedule calls. each schedule have 1 unique session. In my case initially schedule session will be empty. If two or more members wants to join in schedule call they will click on start button. at that time i am checking session exists or not in table. If it is empty then i am generating session and updating session in db and returning that session.
But some times, multiple users clicks on start button at same time, they are going to else condition and updating different sessions and returning with respective session id. then they are redirecting to different session rooms. So how can i prevent this and return same session for all users for single schedule calleven they clicks at same time.
Below is my code which resulting different sessions for users when they click on same time.
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
if(isset($schInfo ->session) && !empty($schInfo ->session))
{
$schedule_session = $schInfo ->session;
} else
{
$schedule_session = CreateSession();
$res = Schedule::where('id', $scheduleId)->update(['session' => $schedule_session]);
}
return $schedule_session;
I have updated this like below. But i am not sure this is the right solution
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
if(isset($schInfo ->session) && !empty($schInfo ->session))
{
$schedule_session = $schInfo ->session;
} else
{
$schedule_session = CreateSession();
sleep(rand(1,5));
$res = Schedule::where('id', $scheduleId)->whereNull('session')->update(['session' => $schedule_session]);
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
$schedule_session = $schInfo ->session;
}
return $schedule_session;
In 1st code snippet out of 10 attempts 8 times generating different sessions. Using second snippet out of 10 times 1 or 2 times generating different sessions.
Please let me know is there any way to do?
This is no way a code solution, the code will not be use-able. However, the logic will be. Your issue is that you store the session before queuing the users to join the call. You should first make the user join a queue, then the hosting caller checks the queue every n seconds for participants (n seconds will allow time for multiple click issues). Then use the first participant in the calling queue to generate the session and append to the call.
You'll need feedback to the participant joining the call which I have not provided in this, but hopefully you get the gist of what I'm saying.
Front-End example of usage:
$(function() {
// Pull out the first connection in the queue, if any
var checkIncomingCallConnections = setInterval(function() {
$.get('/api/calls/incoming', function(response) {
if(response.hasUser()) {
// Your logic for updating or creating the call interface
// ...
clearInterval(this);
}
});
}, 1000);
$('#call-btn').click(function() {
// Generate the queue...
$.post('/api/calls/outgoing', function(response) {
// Your logic for updating or creating the call interface
// ...
checkIncomingCallConnections();
});
});
})(jQuery);
Back-End example of usage:
$schInfo = Schedule::where('status', '=', 1)->where('id', '=', $scheduleId)->first();
if(isset($schInfo ->session):
$schedule_session = $schInfo->session;
else:
$schedule_session = CreateSession();
$res = Schedule::where('id', $scheduleId)->update(['session' => $schedule_session]);
endif;
return $schedule_session;
API Outgoing:
Route::post('/calls/outgoing', function() {
CallQueue::create(referenceAboveCode()); // Should return $schedule_session
});
Route::get('/calls/incoming', function() {
$queue = CallQueue::list(referenceAboveCode()); // Should return $schedule_session
// $queue[0] joins the call
// update database with this session
});

PHP table + pdfMake + AngularJS

I'm a noobie of PHP and AngularJS.
I have a webpage that communicates to a web serves with PHP - AJAX. It queries a database, and echoes the result (a big table) in an html placeholder.
I want to print the content of that table in a downloadable PDF file when the user pushes a button.
I want to use PDFmake and now it works well for test purpose, but how can I pass that content of my table to AngularJS' app?
Maybe should I pass table's id to docDefinition content? In that case I don't know how to do that.
Note: Maybe my approach is uncorrent cause I have to relegate PHP to different tasks and use AngularJS to query the Database, but for now I want to mantain this approach.
Thank You
I suggest you use an angular service (as explained in the docs
)
var bigTableApp = angular.module('bigTable',[])
bigTableApp.factory('BigTableSrv', ['$resource',
function($resource) {
return $resource('URL_to_php_backend', {}, {
query: {
method: 'GET',
params: {param1: 'value 1', param2: 'value 2'},
isArray: true
}
});
}
]);
Then, you can use it in a controller to fetch data from the back-end and build a table structure in PDFmake's table format:
bigTableApp.controller('BigTableController', ['$scope', 'BigTableSrv',
function BigTableController($scope, BigTableSrv) {
$scope.bigTable = BigTableSrv.query();
$scope.pdfMakeTable = {
// array of column widths, expand as needed
widths: [10, *, 130],
body: []
};
$scope.printTable = function() {
pdfMakeTable.body = $scope.bigTable.map(el => {
// process each element of your "big table" to one line of the
// pdfMake table, size of return array must match that of the widths array
return [el.prop1, el.prop2, el.prop3]
});
// create the pdfMake document
let docDefinition = {
content: [ pdfMakeTable ]
}
// print your pdf
pdfMake.creatPdf(docDefinition).print()
}
}
]);

How to retrieve the total number of customers (without a "limit") with the stripe API

I'm working with the PHP stripe API to retrieve a list of all customers for a particular stripe account. I just need the email address of the customer object.
The following function works well however it is only returning 10 customers.
function getListOfCustomers($stripe){
\Stripe\Stripe::setApiKey($stripe['secret_key']);
$list_of_customers = \Stripe\Customer::all(array());
return $list_of_customers['data'];
}
After reading here about the API it tells me that the "limit" parameter (i.e \Stripe\Customer::all(array("limit" => 3)); ) is optional and the "default limit is 10".
So I guess this is why it is returning only 10 customers.
I would like to return an unlimited amount of customers.
I was wondering does anybody know exactly how to do this?
I read also the following on the same page:
You can optionally request that the response include the total count of all customers that match your filters. To do so, specify
include[]=total_count in your request.
However it doesnt tell me exactly how to "include this in my request".
I tried the following however I am receiving syntax errors.
$list_of_customers = \Stripe\Customer::all(array(), include[]=total_count);
and I also tried:
$list_of_customers = \Stripe\Customer::all(array(include[]=total_count));
Thanks for the help.
Stripe does not support getting the total count of objects anymore as the feature has been deprecated for a while.
Instead, they recommend looping over all the customers to count them or find a specific one you want. This is really easy with auto-pagination Your code would look like this:
$customers = \Stripe\Customer::all(array("limit" => 100));
foreach ($customers->autoPagingIterator() as $customer){
echo "Current customer: $customer";
}
It won't store all the customers at once in $customers, only a page. But when you reach the last one in that page the iterator will automatically fetch the next page for you.
Not exactly for the Customer object, but I was working with the Event object and got it to retrieve all the Event (more than the 100 limit) by writing a recursive function in javascript. Notice how I am using the 'hasMore' field to pull in the next set of 100 Events until 'hasMore' == false.
const stripe = require('stripe')(process.env.STRIPE)
module.exports = async function importCanceledSubscription ({$models}) {
const {StripeEvent} = $models
async function createEvents (last_id) {
const {events, hasMore, nextId} = await getStripeEvents(last_id)
let error = false
for (const e of events) {
const stripeObj = new StripeEvent
stripeObj.id = e.id
stripeObj.object = e.object
stripeObj.api_version = e.api_version
stripeObj.created = e.created
stripeObj.type = e.type
stripeObj.livemode = e.livemode
stripeObj.pending_webhooks = e.pending_webhooks
stripeObj.request = e.request
stripeObj.data = e.data
try {
await stripeObj.save()
} catch (e) {
console.log('duplicate found')
error = true
break
}
}
if (hasMore && !error) {
await createEvents(nextId)
}
}
await createEvents()
}
function getStripeEvents (last_id) {
return new Promise((resolve) => {
stripe.events.list({limit: 100, type: 'customer.subscription.deleted', starting_after: last_id}, (err, event) => {
if (err) console.log(err)
const events = event.data
const nextId = event.data[event.data.length - 1].id
resolve({nextId, events, hasMore: event.has_more})
})
})
}

Ajax request takes too long to complete when working with large database

I am working on a website written in Yii framework (version 1.1.14) that allows uploading and displaying news. The admin of the site can select three news to promote to homepage and specify the order in which they are displayed. I am using Mysql database. The news table has two fields: isChecked (0 or 1) and homepagePos (integer) in addition to the other fields. The isChecked field determines whether the news is selected for displaying in homepage and the homepagePos field determines the order in which the news are displayed. I have used jquery's sortable plugin to sort the news. When the user selects which news to display and clicks save button, the news ids are sent to php via ajax.
The javascript portion to send the values to news controller is as follows:
$(document).on('click', '#saveToHomepage', function()
{
var url = ajaxRequestSendUrl; //ajaxRequestSendUrl contains url to news controller's promote to homepage method.
$.ajax({
method: "GET",
url: url,
data: {
contentIds: contentIds, //contentIds contains an array of news Ids in certain order
},
success: function() {
// Show success message
},
error: function() {
alert('Some error occured. Please reload the page and try again.');
}
});
});
Here's the promote to homepage method in news controller:
public function actionHomepage()
{
$allNews = News::model()->findAll();
$value = $_GET['contentIds'];
foreach ($allNews as $news) {
if($news->id == $value[0] ||$news->id == $value[1] ||$news->id == $value[2])
{
$news->isChecked = 1;
$news->homepagePos = array_search($news->id, $value); //Assign index of the array as the position
$news->save();
}
else
{
$news->isChecked = 0;
$news->homepagePos = -1;
$news->save();
}
}
}
My problem is that the news table I have has over 2k data. So the ajax call takes really long time (over a minute) to complete. Is there any way I can optimize the code or is there other way I can approach this to reduce the time taken to complete this operation?
Thanks in advance
Three queries: One first to set the whole table to not checked status, and the rest to set the checked status only in the row each selected id
public function actionHomepage()
{
$values = $_GET['contentIds'];
$sql = "UPDATE news SET idChecked=0,homepagePos = -1";
Yii::app()->db
->createCommand($sql)
->execute();
for($ii = 0; $ii < 3; $ii++) {
$sql = "UPDATE news SET idChecked = 1,homepagePos = ':homepagePos' WHERE id=:id";
Yii::app()->db
->createCommand($sql)
->bindValues(array(':homepagePos' => array_search($ii, $values), ':id' => $values[$ii]))
->execute();
}
}
i am not sure, but why don't you get the filtered records from the database itself by sending the id's of selected news. I don't know your backend language. Seems, you are getting all the records and applying filtering. which consumes time. Instead get the filtered news from the database.
Hope this helps!
Its taking a long time because you're fetching all the records and then updating those three id. You could try this:
$criteria = new CDbCriteria;
$criteria->addInCondition( "id" , $value ) ; // $value = array ( 1, 2, 3 );
News::model()->updateAll(array('isChecked'=>'1','homepagePos'=>'val2'), $criteria);
You could do an update before hand to reset all the homepagePos and then update only the ids you need.

BackboneJS + Codeigniter RESTful API - How do I pass $startdate and $enddate

I have a Backbone App with Codeingiter as Backend. I use the RESTful API setup to pass data back and forth between these to Frameworks.
Now I want to have a View which shows me the "newest followers", for that I created an API like this:
public function new_artist_followers_get($start_date, $end_date)
{
$this->load->database();
$sql = "SELECT users.img_name FROM artist_followers INNER JOIN artists ON artists.artist_id = artist_followers.artist_id INNER JOIN users ON users.user_id = artist_followers.user_id
WHERE artist_followers.artist_id = artists.artist_id AND date_time BETWEEN '$start_date' AND '$end_date' LIMIT 20";
$query = $this->db->query($sql);
$data = $query->result();
if($data) {
$this->response($data, 200);
} else {
$this->response(array('error' => 'Couldn\'t find any artist followers!'), 404);
}
}
My issue is that I'm not really sure how to pass the dates to my Backbone frontend? Do I have to do it somehow like this?:
NewFollowers.NewFollowersCollection = Backbone.Collection.extend({
url: function() {
return '/projects/testproject/index.php/api/testfile/new_artist_followers/'+ this.artist_id + this.startdate + this.enddate;
}
});
Normally, I fetch an API exactly like in the example above, just without the this.startdate and this.enddate and then in my MainView i gather everything, where I for each API/Collection do this (in this case an artist biography):
beforeRender: function() {
var artistbioCollection = new Artistbio.ArtistbioCollection();
artistbioCollection.artist_id = this.artist_id;
this.insertView('.artistBio', new Artistbio.View({collection: artistbioCollection}));
artistbioCollection.fetch();
....etc. etc. ...
}
So can anyone help me out?
Backbone.Collection fetch method accepts extra parameters, they should be passed like this:
artistbioCollection.fetch({
data: {
start_date: this.startdate,
end_date: this.enddate
}
});
It is in Backbone documentation
So here, data is the same property as jQuery.ajax data property, you can then grab these values server-side as usual.
As fetch perfoms a GET request, all parameters passed to data will be appended to query string
You should use URI templates to define the URI on the server side, like so:
http://example.com/api{/artist,id}/followers{?stardate,enddate}
After that you can use for example this library to fill this template on the client side with params. You can add a custom setter for those params, for example (not tested):
NewFollowers.NewFollowersCollection = Backbone.Collection.extend({
url: function() {
return URI.expand("http://example.com/api{/artist,artistId}/followers{?startDate,endDate}", this.params).href();
},
setParams: function (artist, start, end){
this.params = {
artistId: artist.get("id"),
startDate: start,
endDate: end
};
}
});
Be aware, that this is not a complete REST solution. By REST you get hypermedia responses, which contain links. One of those links can contain the actual URI template and a parameter description. So your client is completely decoupled from the URI structure, it does not know how to build an URI, but it knows how to evaluate an URI template, which is a standard solution. You decouple the client from the implementation of the service using standard solutions, this is called the uniform interface constraint of REST.

Categories