Upload images with conversion and new name conventions
Upload images with conversion and new name conventions
Hello
I am uploading multiple files in an editing process and would like to perform some actions before saving the newly uploaded images to the folder and database. Here's my approach:
Initially, I will check whether any images already exist for the same ID in the 'products_files' table. If there are, I'll select the row with the highest ID and add +1 to the file name using the value of the 'id' column from the selected row.
Next, I will convert the uploaded image to the webp format.
Finally, I will save the changes to the 'files' and 'products_files' tables.
Below is my code in the products controller:
```php
<?php
session_start();
/*
* Example PHP implementation used for the index.html example
*/
// DataTables PHP library
include( "../lib/DataTables.php" );
// Alias Editor classes so they are easy to use
use
DataTables\Editor,
DataTables\Editor\Field,
DataTables\Editor\Format,
DataTables\Editor\Mjoin,
DataTables\Editor\Options,
DataTables\Editor\Upload,
DataTables\Editor\Validate,
DataTables\Editor\ValidateOptions;
function logChange ( $db, $action, $id, &$values ) {
$db->insert( 'log', array(
'user' => $_SESSION['name'],
'action' => $action,
'values' => json_encode( $values ),
'row' => $id,
'when' => date('Y-m-d H:i:s')
) );
}
// Get the maximum counter value for the product ID
$result = $mysqli->query("SELECT MAX(counter) FROM products_files WHERE product_id = {$_POST['id']}");
// Fetch the result and increment the counter
$lastCounter = $result->fetch_row()[0];
$lastCounter = ($lastCounter !== null) ? $lastCounter + 1 : 1;
// Get the number of existing images for the product ID
$result = $mysqli->query("SELECT COUNT(*) FROM products_files WHERE product_id = {$_POST['id']}");
$numberOfImages = $result->fetch_row()[0];
// Construct the dynamic filename with counter and image count
$imageCount = $numberOfImages + 1;
$filename = "1_{$lastCounter}of{$imageCount}.webp";
$maxFileSize = 1000000;
$allowedExtensions = array('png', 'jpg', 'jpeg', 'gif');
// Initialize the upload instance
$upload = Upload::inst($_SERVER['DOCUMENT_ROOT'].'/modale/uploads/'.$filename)
->db('files', 'id', array(
'filename' => Upload::DB_FILE_NAME,
'filesize' => Upload::DB_FILE_SIZE,
'web_path' => Upload::DB_WEB_PATH,
'system_path' => Upload::DB_SYSTEM_PATH
))
->validator(Validate::fileSize($maxFileSize, 'Files must be smaller than '.($maxFileSize/1000).' KB'))
->validator(Validate::fileExtensions($allowedExtensions, 'Please upload an image of type: '.implode(", ", $allowedExtensions)));
$upload->validator(function ($file) {
$type = mime_content_type($file['tmp_name']);
if (!in_array($type, array('image/jpeg', 'image/png', 'image/gif'))) {
return 'Invalid file type';
}
// Use ImageMagick to convert the uploaded image to WebP format
$im = new Imagick();
$im->readImage($file['tmp_name']);
$im->setImageFormat('webp');
$im->writeImage($file['tmp_name']);
return true;
});
// Build our Editor instance and process the data coming from _POST
Editor::inst( $db, 'products' )
->fields(
Field::inst( 'id' ),
Field::inst( 'image' ),
Field::inst( 'internalcode' ),
Field::inst( 'batch' ),
Field::inst( 'brand' )
->validator( Validate::notEmpty( ValidateOptions::inst()
->message( 'A Brand is required' )
) ),
Field::inst( 'vendorcode' ),
Field::inst( 'costprice' )
->validator( Validate::numeric() )
->setFormatter( Format::ifEmpty(null) ),
Field::inst( 'sellingprice' )
->validator( Validate::numeric() )
->setFormatter( Format::ifEmpty(null) ),
Field::inst( 'name' )
->validator( Validate::notEmpty( ValidateOptions::inst()
->message( 'A Name is required' )
) ),
Field::inst( 'collection' ),
Field::inst( 'modale_category' )
->validator( Validate::notEmpty( ValidateOptions::inst()
->message( 'A Category is required' )
) ),
Field::inst( 'modale_subcategory' )
->validator( Validate::notEmpty( ValidateOptions::inst()
->message( 'A Sub Category is required' )
) ),
Field::inst( 'dimensions' ),
Field::inst( 'designedby' ),
Field::inst( 'materialfinishes' ),
Field::inst( 'specifications' ),
Field::inst( 'description' ),
Field::inst( 'catalog_type' )
->validator( Validate::notEmpty( ValidateOptions::inst()
->message( 'A Catalog Type is required' )
) ),
Field::inst( 'colors' ),
Field::inst( 'unit' ),
Field::inst( 'weight' ),
)
->join(
Mjoin::inst( 'files' )
->link( 'products.id', 'products_files.product_id' )
->link( 'files.id', 'products_files.file_id' )
->fields(
Field::inst( 'filename' ),
Field::inst( 'id' )->upload( $upload )
)
)
->on( 'postCreate', function ( $editor, $id, &$values, &$row ) {
logChange( $editor->db(), 'create', $id, $values );
} )
->on( 'postEdit', function ( $editor, $id, &$values, &$row ) {
logChange( $editor->db(), 'edit', $id, $values );
} )
->debug(true)
->process( $_POST )
->json();
Answers
Thanks for posting this. It is worth noting that you have to be careful doing this - say you had two users at the same time on the site, both uploading an image at the same time. The +1 might be assigned twice! You need a database transaction to stop that happening, or better yet would be to store a hash of the file in the database and look that up to see if the file already exists (then the file name doesn't make any difference).
Allan
Yes, you are right. I will do what you have suggested. My issue here that how can get the value of the cell outside Editor::inst( $db, 'products' ). fro example below:
```
$name = Field::inst( 'name' );
Editor::inst( $db, 'products' ){
....
}```
Using the events, such as
preCreate
andpreEdit
would be the best way to do it I think.Allan
Hello,
Thank you. But how can I assign an attribute for example $name to a value from the editor on preCreate or preEdit?
With a file upload it is a little more tricky since the upload action is async to the rest of the form. Let me check I'm understanding what you are trying to do first - you want to use, say, the file name and store that into another field in the database table for the host editor?
A left join would be the way to get the file name (or any other file information), using a reference from the table you are editing to a files host table. Then you don't need to copy anything over and retain referential integrity.
I may have missed the mark though?
Allan