Convert Upload to UploadMany

Convert Upload to UploadMany

joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

Hello,

I can't figure out how to get the upload to work with multiple files. I've tried several methods here on the forum, but it always gives me errors or "Uncaught Error: Upload collections must have an array as a value", if I use the name with array it gives me the error: "Uncaught Error: Call to undefined method DataTables\Editor \Field::db() in ...".

Can anyone help me with this example?
.js

{
                label: "Imagen:",
                name: "imagem",
                type: "upload", 
                display: (fileId, counter) => '<img src="'+table.file( 'files', file_id ).web_path+'"/>',
                clearText: "Clear",
                ajax: {
                    url: "includes/table.sells.php",
                    type: "POST",
                    enctype: 'multipart/form-data',
                    processData: false,
                    contentType: false,
                },
                noImageText: 'No image',
                noFileText: 'No images',
            }, 

Then in .php:

Field::inst( 'imagem' )
            ->setFormatter( 'Format::nullEmpty' )
             >upload(
                    Upload::inst(  function ( $file, $id ) use ( $varArray, $db ) {
                      move_uploaded_file( $file['tmp_name'] , $varArray["sysPath"] .$id. '.jpg' );
                        make_thumb($varArray["sysPath"] .$id. '.jpg', $varArray["sysPathThumbs1"].$id. '.thumb.jpg', 480);
                      $db->update(
                        'files', // Database table to update
                        [
                         "web_path" => $varArray["webPath"] .$id. '.jpg',
                         "system_path" => $varArray["sysPath"]  .$id. '.jpg', 
                         "web_path_thumb1" => $varArray["webPathTumbs1"] .$id. '.thumb.jpg',
                         "system_path_thumb1" => $varArray["sysPathThumbs1"]  .$id. '.thumb.jpg', 
                        ],
                        [ "id" => $id ]
                      );
                      return $id;
                    } )  
                ->db( 'files', 'id', array(
                    'site'          =>'',
                    'filename'    => Upload::DB_FILE_NAME,
                    'filesize'    => Upload::DB_FILE_SIZE,
                    'web_path'    => '',
                    'system_path' => '',
                    'web_path_thumb1'    => '',
                    'system_path_thumb1' => '',
                ) )
                ->validator( function ( $file ) {
                    return$file['size'] >= 3000000 ?
                        "Files must be smaller than 3000K o 3,0 Mb" :
                        null;
                } )
                ->dbClean( function ( $data ) {
                                // Remove the files from the file system
                                for ( $i=0, $ien=count($data) ; $i<$ien ; $i++ ) {
                                    unlink( $data[$i]['system_path'] ); // upload file  folder
                                    unlink( $data[$i]['system_path_thumb1'] );  //my thumbnails  file folder #1
                                }
                                // Have Editor remove the rows from the database
                                return true;
                            } )
                ->allowedExtensions( array(  'jpg'  ), "Please upload an image file, ONLY .jpg " )
 
            ),

This question has accepted answers - jump to:

Answers

  • rf1234rf1234 Posts: 2,996Questions: 87Answers: 421

    Your Javascript is only for single uploads. You would need something like this:

    {
                    label: "Imagen:",
                    name: "imagem[].id",
                    type: "uploadMany",
                    display: (fileId, counter) => '<img src="'+table.file( 'files', file_id ).web_path+'"/>',
                    clearText: "Clear",
                    ajax: {
                        url: "includes/table.sells.php",
                        type: "POST",
                        enctype: 'multipart/form-data',
                        processData: false,
                        contentType: false,
                    },
                    noImageText: 'No image',
                    noFileText: 'No images',
                },
    

    Read the docs please:
    https://editor.datatables.net/reference/field/uploadMany

    https://editor.datatables.net/examples/advanced/upload-many

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    Hello rf1234, thank you. I've already tried this way, the problem is that I get an error saying that this name image[] does not exist. Regarding the docs, I have already analyzed them, I have even tried the examples described there. Can you review the php code, please? About db, it's all the same when using only one file, right? When I using your code in the db de value is "array" and give error. When using one image, appear the value corrected ("4" for example).

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    And please, help me understand how different is the 2 manners, what's changes about single and multiple... To learn and then to know how to optimize the project. Thank you a lot!

  • rf1234rf1234 Posts: 2,996Questions: 87Answers: 421
    edited November 2023 Answer ✓

    I never use single upload, only multiple upload on very many occasions. It is rather straightforward I would say. If the examples aren't instructive enough here is one from my own coding:

    }, {    
        label: lang === 'de' ? 'Dokumentation:' : 'Documentation:',
        name: "file[].id",
        type: "uploadMany",
        display: function ( fileId, counter ) {
            var fileNameExt = contractEditor.file( 'file', fileId ).name;
            return renderFilesEditor(fileNameExt);
        },
        dragDropText: dragDropText,
        uploadText:   uploadText,
        noFileText:   noFileText,
        processingText: processingText,
        fileReadText: fileReadText
    }, {
    

    the most important line is

    name: "file[].id". 
    

    This is the array of files the user can upload. It can be empty or contain multiple values.
    In your PHP code I do not see a join! Without a join this is never going to work. The join must be between the files table and the contract table in my example. Don't know what the tables are called in your example. You will need a link table as well to do that join. In my example the link table is called "contract_has_file".

    So this is my PHP:

    ->join(
    Mjoin::inst( 'file' )
        ->link( 'contract.id', 'contract_has_file.contract_id' )
        ->link( 'file.id', 'contract_has_file.file_id' )
        ->fields(
            Field::inst( 'id' )
            ->upload( Upload::inst( dirname($_SERVER['DOCUMENT_ROOT']).'/lgfuploads/contracts/__ID__.__EXTN__' )
                    ->db( 'file', 'id', array(
                        'soft_deleted'  => 0, 
                        'about'         => 'C',  //contract
                        'name'          => Upload::DB_FILE_NAME,
                        'size'          => Upload::DB_FILE_SIZE,
                        'web_path'      => Upload::DB_WEB_PATH,
                        'system_path'   => Upload::DB_SYSTEM_PATH,
                        'creator_id'    => $_SESSION['id']
                    ) )
                    ->validator( function ( $file ) use ( $msg ) {
                        if ($file['size'] >= 52428800) {
                            return $msg[3];
                        } else {
                            return true;
                        }
                    } )
                    ->allowedExtensions( array  //php is not case sensitive here
                      ( 'pdf', 'xls', 'xlsx', 'csv', 'doc', 'docx', 'rtf', 'ppt',  
                        'pptx', 'odt', 'ods', 'odp' ), $msg[2] )
            ),
            Field::inst( 'soft_deleted' )->set( false ), 
            Field::inst( 'creator_id' )->set( false ), 
            Field::inst( 'update_time' )->set( false ), 
            Field::inst( 'web_path' )->set( false ),
            Field::inst( 'name' )->set( false )             
        )
    )
    

    And a screenshot from my data model:

    You can see multiple "..._has_file" tables. Those are the link tables used in the Mjoin.

    Good luck.

  • allanallan Posts: 63,522Questions: 1Answers: 10,473 Site admin
    edited November 2023

    I just wanted to add that if you are still having problems with this, could you show me the JSON that is being returned by the server when the table is initially loaded please? Showing the Javascript you are using for the uploadMany would be useful as well.

    Thanks,
    Allan

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3
    edited November 2023

    To my shame, I haven't managed to get there yet...

    I have a database table named "sells" with a customer sells list that contains columns:
    id | date | contact_id | imagem
    where the "imagem" column indicates whether that row has files or not.

    I also have another table named "files123" where the locations of the images will be stored. It contains the columns:
    id | filename | filesize | web_path | system_path
    I took these column names from a forum example. (I renamed them for better understanding, avoiding conflicts with 'file' and similar words.)

    I might be confusing "file" and "files," and I'm unsure about the use of "_id" and ".id."

    Do I need to perform a join between the two tables? Initially, I thought I would just retrieve the "imagem" column from the "sells" table and get this value from the "files123" table. Although, as I write this sentence, it makes sense to have a function for this :D

    I apologize for asking, but could you modify your example to use the information I provided above? I know it might seem odd and ugly to ask someone to "write my code," but I'll understand it better with the logic, just as I've learned from examples in the forum and documentation (except for this and select2, which are giving me a headache) :D

  • rf1234rf1234 Posts: 2,996Questions: 87Answers: 421
    Answer ✓

    No, you can't do it that way. You must use a link table for "uploadMany" because otherwise you cannot use the Mjoin which is required. Take a look at the picture from my data model above.

    You have three tables.
    a) contract
    b) contract_has_file
    c) file

    b) is just the link between "contract" and "file" to allow one contract to have many files. Yes you could also resolve this with a 1:N relationship with "file" containing the foreign key from "contract". But that doesn't work with an Mjoin!

    Whether you call your file table "file" or "files" doesn't matter. I always use singular. But you can do what you like.

    You would need a table "sale" or "sales", a table "file" or "files" and a table "sale_has_file". The column "imagem" to indicate whether a record has a file or not is not needed. For that you have the Mjoin which will return nothing if there is no file for the sale.

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    Thank very much!! I think I understood.

    I thought it was only necessary to reference 1 column as it works in single file upload. Apparently everything is working, or it should be, but this time there are errors in the Javascript:

    Uncaught Error: No Ajax option specified for upload plugin at Kb.b.ajax.h.onload

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    I need to add a new ajax inside that code again? I have the same in top

    {   
                label: 'Documentation:',
                name: "file[].id",
                type: "uploadMany",
                display: function ( fileId, counter ) {
                    var fileNameExt = contractEditor.file( 'file', fileId ).name;
                    return renderFilesEditor(fileNameExt);
                },
                dragDropText: "dragDropText",
                uploadText:   "uploadText",
                noFileText:   "noFileText",
                processingText: "processingText",
                fileReadText: "fileReadText",
                ajax: {   <--- this one?
                        url: 'includes/table.sellls.php', 
                        type: 'POST',
                        dataType: 'json',
                        data: function (d) {
                            d.myCustomData = 'valor'; // i dont know waht is this :D 
                        },
                    }
            }
    

    I have the same in top of script when start editor:

    editor = new $.fn.dataTable.Editor({
        table: "#tbbl",
        ajax: function (method, url, data, success, error) {
            $.ajax({
                type: "POST",
                dataType: "json",
                data: data,
                url: 'includes/table.sells.php...
    
    
  • rf1234rf1234 Posts: 2,996Questions: 87Answers: 421

    maybe @allan can help you with that issue?!

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    Thank you rf1234 for all your help!

    Already solved, whit the ajax marked as "this one"... i just want to learn why i need another ajax info in uploadMany type.

  • rf1234rf1234 Posts: 2,996Questions: 87Answers: 421

    Already solved, whit the ajax marked as "this one"... i just want to learn why i need another ajax info in uploadMany type.

    Ok, now I am getting it! I think you don't need an additional ajax specification for uploadMany. I don't use one either.

    This is a simple example from my own coding allowing users to safely upload documentation instead of sending it by email.

    var dragDropText;
    var uploadText;
    var noFileText;
    var processingText;
    var fileReadText;
    if (lang === 'de') {
        dragDropText = 'Drag und Drop zum Hochladen';
        uploadText = 'Dokument auswählen ...';
        noFileText = 'Keine Dokumente';
        processingText = 'Verarbeitung läuft ...';
        fileReadText = 'Dokument wird hochgeladen';
    } else {
        dragDropText = 'Drag and Drop to Upload';
        uploadText = 'Choose Document ...';
        noFileText = 'No Documents';
        processingText = 'Processing ...';
        fileReadText = 'Uploading Document';
    }
    
    uploadEditor = new $.fn.dataTable.Editor({
        ajax: {
            url: 'actions.php?action=tblUpload'
        },
        table: "#tblUpload",
        fields: [
                 {
                label: lang === 'de' ? 'Bezeichnung:' : 'Name:',
                name: "document.label"
            }, {
                type: "hidden",
                name: "document.type"
            }, {
                label: lang === 'de' ? 'Dokument(e):' : 'Document(s):',
                name: "file[].id",
                type: "uploadMany",
                display: function ( fileId, counter ) {
                    var fileNameExt = uploadEditor.file( 'file', fileId ).name;
                    return renderFilesEditor(fileNameExt);
                },
                dragDropText: dragDropText,
                uploadText:   uploadText,
                noFileText:   noFileText,
                processingText: processingText,
                fileReadText: fileReadText
            }
        ]
    });
    
    uploadEditor
            .on('open', function () {
                this.set( { 'document.type': 1 } ); //upload documents
            })
    
    
  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    I have this one:

    editor = new $.fn.dataTable.Editor( {
        table: "#tbbl",
        ajax:function(method, url, data, success, error) {
            $.ajax({
                type: "POST",
                dataType: "json",
                data:data,
                url: 'includes/table.sells.php',
                success: function (response) {
                    success( response );
                },
                error: function (xhr, err, thrown) {
                    if (xhr.status == 403) {
                         window.location.href = "entrar?P=" + window.location.pathname.split('/').filter(part => part).pop();;
                    } else {
                        error( xhr, err, thrown );
                    }
                }
            })
        },  
    

    if i dont put ajax again, give the error:
    Uncaught Error: No Ajax option specified for upload plugin at Kb.b.ajax.h.onload

  • joseoliveiraborlasjoseoliveiraborlas Posts: 80Questions: 8Answers: 3

    After rereading both, I understood that I am using an additional function... this is probably what interrupts the editor's defaults, right?
    ajax:function(method, url, data, success, error) {

  • rf1234rf1234 Posts: 2,996Questions: 87Answers: 421
    edited November 2023

    Probably yes, but I am not an expert on this. I use the "ajax" method in a simple version only ...

    I also use it to post variables to the server like this, but that is as complex as it gets in my code ...

    This passes the selected "contract_id" to the server so that it can be used as $_POST['contract_id'] in PHP.

    var infomaContractEditor = new $.fn.dataTable.Editor( {
        ajax: {
            url: infomaContractDataUrl,
            data: function ( d ) {
                if ( ! accountingInterfacePages ) {
                    var selected = contractTable.row( {selected: true} ); 
                    if (selected.any()) {
                        d.contract_id = selected.data().contract.id;
                    }
                }
            }
        },
    
  • allanallan Posts: 63,522Questions: 1Answers: 10,473 Site admin

    this is probably what interrupts the editor's defaults, right?

    It does - the upload request would go through your custom function, which is probably causing the issue since it isn't handling file upload.

    What you can do is specify an ajax property on the object for the uploadMany object with just a string if you want the field's default handling Ajax to handle it (and keep your custom function at the top level).

    Allan

Sign In or Register to comment.