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
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

  • allanallan Posts: 61,436Questions: 1Answers: 10,049 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: 61,436Questions: 1Answers: 10,049 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: 61,436Questions: 1Answers: 10,049 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.