Upload images with conversion and new name conventions

Upload images with conversion and new name conventions

gatesnetgatesnet Posts: 13Questions: 5Answers: 0
edited March 2023 in Free community support

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();

<?php > ``` ?>

Answers

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    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

  • gatesnetgatesnet Posts: 13Questions: 5Answers: 0

    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' ){
    ....

    }```

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    Using the events, such as preCreate and preEdit would be the best way to do it I think.

    Allan

  • gatesnetgatesnet Posts: 13Questions: 5Answers: 0

    Hello,

    Thank you. But how can I assign an attribute for example $name to a value from the editor on preCreate or preEdit?

  • allanallan Posts: 63,523Questions: 1Answers: 10,473 Site admin

    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

Sign In or Register to comment.