File Upload - Editing file details

File Upload - Editing file details

TooManyTablesTooManyTables Posts: 23Questions: 7Answers: 1

I've recently had a feature request, to allow people to be able to add files ('Resources') to a particular piece of equipment. They want to be able to edit some details about the file (E.g. give it a description, set a category etc.).

This use case seems to be a bit different from the examples presented on the site, and I've run into a bit of difficulty when implementing the upload feature with DataTables. In the examples, we seem to be uploading the file automatically and editing the details of a separate but related record (like a User). What I need to do is not only upload the file but then edit the details of that file's record, rather than, say, the Equipment it is attached to. A record in the linking table 'EquipmentResources' also needs to be created. The DataTable this code is attached to has one row per file - see attached.

The problem is that when I run the code below, I end up with two records in Resources, each containing only some of the information entered. It seems like the ->upload(->db()) command creates one record, with the paths, but pressing 'Create' afterwards creates another with the user-entered information on the form. Essentially, I need to be able to get the just-created Resource record when the file is uploaded, so that the information that the user has entered (Description, type etc) can be added to it when the user hits 'Create'.

Is that actually possible? Or is there a better way of doing this?

Also, is there any way to not have the file upload immediately upon selection? If a user cancels out of the dialog, the file remains on the server, as does the freshly-created (but half empty) Resources record. Ideally, the file and the record would only get created when the user confirms they actually want to create or update the record.

Database Structure (SQL Server)

==Equipment (simplified)==
equipmentID (PK)

==EquipmentResources==
equipmentID (PK; FK, Equipment)
resourceID (PK; FK, Resources)

==Resources==
resourceID (PK - Identity, seed 1)
resourceFileName
resourceDescription
resourceType
resourceUploadDate
resourceWebPath
resourceSystemPath

Client-side Editor code

$(document).ready(function() {
        
        var resource_editor = new $.fn.dataTable.Editor( {
            ajax: {
            url: equipPath + '/Controllers/UpdateResources.php',
            data: function ( d ) {
                    d.equipmentID = $('#equipmentID').val();
                }
            },
            table: "#resource-table",
            fields: [ 
                {
                    label: 'Equipment ID',
                    name: 'EquipmentResources.equipmentID',
                    def: $('#equipmentID').val(),
                    type: 'hidden'
                }, {
                    label: "File Name:",
                    name: "Resources.resourceFileName"
                }, {
                    label: "File Description:",
                    name: "Resources.resourceDescription",
                    type: 'textarea'
                }, {
                    label: "File Type:",
                    name: "Resources.resourceType",
                    type: "select",
                    options: ["Image", "Resource"],
                    placeholder: ''
                }, {
                     label: "File:",
                    name: "EquipmentResources.resourceID",
                    type: "upload",
                    noImageText: 'No file'
                }
            ]
        } );

});

Server Side PHP Code

$editor =  Editor::inst($db, 'Resources', 'Resources.resourceID')
        ->debug(true)
        ->field(
            Field::inst( 'Resources.resourceID' )
                ->validator( 'Validate::unique' ),
            Field::inst( 'Resources.resourceFileName' )
                ->validator( 'Validate::required' )
                ->validator( 'Validate::maxLen', 150 ),
            Field::inst( 'Resources.resourceDescription' )
                ->validator( 'Validate::required' )
                ->validator( 'Validate::maxLen', 750 ),
            Field::inst( 'Resources.resourceType' )
                ->validator( 'Validate::required' ),
            Field::inst( 'Resources.resourceUploadDate' ),   
            Field::inst( 'Resources.resourceViewableGeneral' )
                ->validator( 'Validate::required' ), 
            Field::inst( 'EquipmentResources.equipmentID' )
                ->validator( 'Validate::notEmpty' ),
            Field::inst('EquipmentResources.resourceID')
                ->upload( Upload::inst( $_SERVER['DOCUMENT_ROOT'] . "\Files\EQUIP\__ID__.__EXTN__") 
                    ->db( 'Resources', 'resourceID', array(
                        'resourceSystemPath' => Upload::DB_SYSTEM_PATH,
                        'resourceWebPath' => Upload::DB_WEB_PATH
                    ) )
                )
                ->setFormatter( 'Format::nullEmpty' )
    )           
    ->leftJoin( 'EquipmentResources', 'Resources.resourceID', '=', 'EquipmentResources.resourceID' )
    ->leftJoin( 'Equipment', 'EquipmentResources.equipmentID', '=', 'Equipment.equipmentID' )
    ->where(function($q) use ($equipmentID) {
        $q->where('EquipmentResources.equipmentID', $equipmentID, '=');
    })
    ->process($_POST)
    ->json();

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 61,452Questions: 1Answers: 10,055 Site admin
    Answer ✓

    Also, is there any way to not have the file upload immediately upon selection?

    This is the key to the whole thing, and I'm afraid that the answer, with the built in upload field type, is no. It will always upload the file immediately and async from the rest of the form. The main reason for that is that the upload request type is somewhat different from the standard Ajax request Editor makes. They could be made to overlap, but I didn't take it in that direction (yet - as with everything software it might change in future).

    Using only the built in options in Editor, what I think you would need to do is have a table for the file info itself, and another table that would contain the meta information entered by the user - left join them together and then let the user edit the meta information table (which also allows an upload that will be saved to the files table).

    Not ideal I realise, but that's currently the only way without writing a custom field type plug-in.

    Allan

  • TooManyTablesTooManyTables Posts: 23Questions: 7Answers: 1

    Not ideal I realise, but that's currently the only way without writing a custom field type plug-in.

    Heh, no worries mate. Can't win 'em all, I suppose.

  • TooManyTablesTooManyTables Posts: 23Questions: 7Answers: 1

    Happy update to this one - I rejiggered things a little, and it ended up working. Doing the 'create' on the linking table seems to be the solution. Which makes sense - looking at it now, I was doing the 'create' on the Resources table, which is obviously going to give me a second record there and none on the linking table. I will blame Friday afternoon for that brain-fart. :s

    With that changed, the code now properly inserts into Resources on upload, then uses the returned ID to insert into EquipmentResources.

    Now, there's obviously still the issue of the user uploading a file and then cancelling out, but I can deal with that.

    Code, for anybody else who needs to do something similar.

    Client-side Code

    $(document).ready(function() {
            
            var resource_editor = new $.fn.dataTable.Editor( {
                ajax: {
                url: equipPath + '/Controllers/UpdateResourcesTest.php',
                data: function ( d ) {
                        d.equipmentID = $('#equipmentID').val();
                    }
                },
                table: "#resource-table",
                fields: [ 
                    {
                        label: 'Equipment ID',
                        name: 'EquipmentResources.equipmentID',
                        def: $('#equipmentID').val(),
                        type: 'hidden'
                    }, {
                        label: "File Name:",
                        name: "Resources.resourceFileName"
                    }, {
                        label: "File Description:",
                        name: "Resources.resourceDescription",
                        type: 'textarea'
                    }, {
                        label: "File Type:",
                        name: "Resources.resourceType",
                        type: "select",
                        options: ["Image", "Resource"],
                        placeholder: ''
                    }, {
                        label: "Public View:",
                        name: "Resources.resourceViewableGeneral",
                        type: 'radio',
                        options: [
                            { label: "Yes", value: 1 },
                            { label: "No",    value: 0 }
                        ],
                        def: 1
                    }, {
                        label: "File:",
                        name: "EquipmentResources.resourceID",
                        type: "upload",
                        display: function ( id ) {
                            return '<img src="'+resource_editor.file( 'Resources', id ).resourceWebPath+'"/>';
                        },
                        noImageText: 'No image'
                    }
                ]
            } );
    });
    

    Server-side Code

    $editor =  Editor::inst($db, 'EquipmentResources', array('EquipmentResources.resourceID', 'EquipmentResources.equipmentID'))
            ->debug(true)
            ->field(
                Field::inst( 'Resources.resourceID' )
                    ->set(false),
                Field::inst( 'Resources.resourceFileName' )
                    ->validator( 'Validate::required' )
                    ->validator( 'Validate::maxLen', 150 ),
                Field::inst( 'Resources.resourceDescription' )
                    ->validator( 'Validate::required' )
                    ->validator( 'Validate::maxLen', 750 ),
                Field::inst( 'Resources.resourceType' )
                    ->validator( 'Validate::required' ),
                Field::inst( 'Resources.resourceUploadDate' )
                    ->set(false),  
                Field::inst( 'Resources.resourceWebPath' ),
                Field::inst( 'Resources.resourceViewableGeneral' )
                    ->validator( 'Validate::required' ), 
                Field::inst( 'EquipmentResources.equipmentID' )
                    ->validator( 'Validate::notEmpty' ),
                Field::inst('EquipmentResources.resourceID')
                    ->upload( Upload::inst( $_SERVER['DOCUMENT_ROOT'] . "\Files\EQUIP\__ID__.__EXTN__") 
                        ->db( 'Resources', 'resourceID', array(
                            'resourceSystemPath' => Upload::DB_SYSTEM_PATH,
                            'resourceWebPath' => Upload::DB_WEB_PATH,
                            'resourceUploadDate' => Date('Y-m-d')
                        ) )
                    )
                    ->setFormatter( 'Format::nullEmpty' )
        )           
        ->leftJoin( 'Resources', 'EquipmentResources.resourceID', '=', 'Resources.resourceID' )
        ->leftJoin( 'Equipment', 'EquipmentResources.equipmentID', '=', 'Equipment.equipmentID' )
        ->where(function($q) use ($equipmentID) {
            $q->where('EquipmentResources.equipmentID', $equipmentID, '=');
        })
        ->process($_POST)
        ->json();
    
  • allanallan Posts: 61,452Questions: 1Answers: 10,055 Site admin

    Awesome - thanks for posting back with your code!

    Allan

This discussion has been closed.