This CRUD application is an illustration of a set of PrimeUI components, released in version 1.0 in October 2013, like the puidatatable, puipaginator, puidialog, puidropdown, puitabview, puipanel or the puibutton component.

It allows you for test purpose, to add, edit, remove and search products without page reload.
From the Settings pane, you can reset the datatable content and modify the number of rows per page.

If you made changes to the data table rows by adding, editing, removing products, its content can be reinitialized from the original data rows by clicking the Reset button below.

The number of rows per page can be increased or decreased dynamically.

Just choose the number of your choice in the list below.

Rows are dynamically loaded in the datatable each time a page is selected, without a browser page refresh.

This feature is available for the puidatatable component when the option lazy: true is activated.

Thus, an Ajax request of type GET can be sent to the web server to retrieve the rows for the page to display, thanks to the jQuery $.ajax() function call, made in the javascript method defined for the datasource datatable option.

The PHP script get_data.php called by the Ajax request, returns the rows in JSON format for the page to be displayed, including the total number of rows for evaluation of the total number of pages to display by the paginator.

Finally, the callback method of the datasource option is invoked with in parameter, the rows returned by the Ajax request, for loading the content of the datatable page.

The datatable rows are sorted when the user click on a column title, if the sort functionality is enabled for the column.

The option sortable:true allows you to activate the sort feature for a column, within the datatable option columns.

When a column title is clicked on, the function defined for downloading the data rows within the option datasource, is triggered with indication of the sort order and the name of the column which has been clicked on.
These indications are then transmitted by an Ajax request to the server PHP script which in turn, sends back the ordered rows.

The paginator is directly managed by the component puidatatable.

When the datatable is declared, the paginator characteristics are set thru the option paginator and its sub-options totalRecords (total number of rows for all pages), rows (number of rows per page) and pageLinks (maximum number of page links displayed on the paginator).

The component puipaginator instanciated by the datatable is static once created.

To enhance the paginator so that its page links become dynamic when the total number of rows has changed, I added to the component for demo purposes, a new method Refresh, called each time that the rows of a page are loaded.

The next release of the PrimeUI components suite is supposed to include such a refresh function.

The data typed in by the user in the edit form is validated by the web browser thanks to the new HTML5 validation capabilities applied to the <input/> tag.

The attribute required is set to mandatory fields prefixed by a * character. So, the form is not submitted while the mandatory fields are not filled.

Concerning the field Price, a regular expression is defined using the attribute pattern to be sure a valid amount has been entered.

To trigger the HTML5 validation process, the save button must be declared within the form with the attribute type="submit".
In addition, the form submit event is intercepted in a jQuery event handler where an Ajax request is sent to the web server instead of the default form HTTP request.
The HTTP default form request is blocked by a call to the jQuery function event.preventDefault().

The search feature consists in filtering the datatable content to the only rows which contain the text entered in the search field.

The component puidatatable has been overriden so that the new function named "filterRows", especially developped for demo purposes, is called in response to the form event submit, with the searched text in parameter, to be finally transmitted to the datasource function, in charge of requesting the corresponding rows to the web server.

The PrimeUI components are in the same time jQuery UI components and therefore can be easily enhanced by adding new methods or by overriding existing methods.

$first = isset($_GET['first']) ? intval($_GET['first']) : 0;
$rows = isset($_GET['rows']) ? intval($_GET['rows']) : 10;
$sort_field = isset($_GET['sortfield']) ? filter_var($_GET['sortfield'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH) : 'product_name';
$sort_order = isset($_GET['sortorder']) ? intval($_GET['sortorder']) : 1;
$criteria = isset($_GET['search_criteria']) ? filter_var($_GET['search_criteria'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH) : null;

$filters = '';

include 'db_connect.php';

if (!empty($criteria)) {
	$filters = " where upper(product_name) like upper('%$criteria%')"; 
	$filters .= " or upper(product_description) like upper('%$criteria%')";
	$filters .= " or upper(product_number) like upper('%$criteria%')";

$result["total"] = 0;
$result["success"] = false;

$query = "select count(*) from products" . $filters;
$rs = mysql_query($query);
if ($rs) {
	$row = mysql_fetch_row($rs);
	$result["total"] = $row[0];

	$sort_field .= $sort_order === 1 ? ' asc' : ' desc';
	$query = "select * from products" . $filters . " order by $sort_field limit $first,$rows";
	$rs = mysql_query($query);
	if ($rs) {
		$products = array();
		while($row = mysql_fetch_object($rs)){
			array_push($products, $row);
		$result["rows"] = $products;
		$result["success"] = true;
		$result["msg"] = 'Products successfuly requested.';
		if (!empty($criteria)) {
			$result["msg"] .= " Filter '$criteria' applied.";
	} else {
		$result["msg"] = 'Sorry, unable to retrieve the rows from the database!';
} else {
	$result["msg"] = 'Sorry, unable to count the total number of rows!';
echo json_encode($result);
$result = false;
$product_id = intval($_REQUEST['product_id']);
$part_number = filter_var($_REQUEST['part_number'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
$product_name = filter_var($_REQUEST['product_name'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
$product_description = filter_var($_REQUEST['product_description'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
$product_price = number_format(floatval($_REQUEST['product_price']), 2, '.', '');

include 'db_connect.php';

$action = $product_id === -1 ? 'new' : 'update';

if ($action === 'new') {
	$sql = "insert into products(part_number,product_name,product_description,product_price) values('$part_number','$product_name','$product_description','$product_price')";
	$success_msg = 'Item added successfully.';
	$error_msg = 'Sorry, some errors occured. Insert aborted!';
} else {
	$sql = "update products set part_number='$part_number',product_name='$product_name',product_description='$product_description',product_price='$product_price' where product_id=$product_id";
	$success_msg = 'Item successfully updated.';
	$error_msg = 'Sorry, some errors occured. Update aborted!';

$result = @mysql_query($sql);

if ($result){
	echo json_encode(array('action'=>$action,'success'=>true,'msg'=>$success_msg));
} else {
	echo json_encode(array('action'=>$action,'success'=>false,'msg'=>$error_msg));
$id = intval($_REQUEST['product_id']);

include 'db_connect.php';

$sql = "delete from products where product_id=$id";
$result = @mysql_query($sql);
if ($result){
	echo json_encode(array('success'=>true,'msg'=>'Product removed successfully.'));
} else {
	echo json_encode(array('success'=>false,'msg'=>'Sorry, some errors occured!'));
$conn = @mysql_connect('','db_user','db_pwd');
if (!$conn) {
	die('Could not connect: ' . mysql_error());
mysql_select_db('database_name', $conn);

Data in the table is loaded dynamicaly by AJAX calls to the web server where the data requests are processed by PHP scripts.

Do you really want to remove the selected product?