1

I am trying to seed some images to an existing volume programmatically. The code I'm using is taken (almost) verbatim from Craft's AssetsController@actionUpload:

        $filename = Assets::prepareAssetName($filename);

        $asset = new Asset();
        $asset->tempFilePath = $path;
        $asset->filename = $filename;
        $asset->newFolderId = $folder->id;
        $asset->setVolumeId($folder->volumeId);
        $asset->uploaderId = User::findOne()->id;
        $asset->avoidFilenameConflicts = true;
        $asset->setScenario(Asset::SCENARIO_CREATE);
        $res = Craft::$app->getElements()->saveElement($asset);

However, this creates a new folder on the root of my project, by the same name as the one that already exists under web, and saves the file there, which results in a broken asset in the CP.

When I upload using Craft's CP the file is uploaded correctly to web/{volumeFolder}.

I have dumped the $asset object both from my plugin code and from Craft's code and compared them. Everything matches (volumeId, folderId, tempFilePath).

I couldn't track the place in the code where the folder is created. I'd appreciate some pointers.

EDIT:

To answer @MoritzLost comment here is the full context:

private function generateImages()
    {
        $assetVolume = Craft::$app->volumes->getVolumeByHandle(Handles::ASSETS);
        $assets = Asset::find()->volume($assetVolume)->all();

        // Generate new assets
        if (count($assets) === 0) {
            // Find folder
            $folder = VolumeFolder::findOne([
                'volumeId' => $assetVolume->id
            ]);

            // images in plugin folder
            $dir = realpath(__DIR__ . '/../assets/site/images');
            $images = ['hero_1', 'img_1', 'img_2', 'img_3', 'img_4'];

            // temp folder in project
            $path = new Path();
            $tempDirPath = $path->getTempPath();

            foreach ($images as $image) {
                // move file from plugin assets to project temp folder
                $filename = $image . '.jpg';
                $filenameUnique = $image . '_' . rand(100, 10000) . '.jpg';
                $path = $dir . DIRECTORY_SEPARATOR . $filename;
                $tempFilePath = $tempDirPath . DIRECTORY_SEPARATOR . $filenameUnique;
                file_put_contents($tempFilePath, file_get_contents($path));


                // Upload asset to permanent folder
                // and create DB record
                $result = $this->uploadNewAsset($folder, $tempFilePath, $filenameUnique);

                $assets[] = $result;
            }
        }

        return array_map(function ($asset) {
            return $asset->id;
        }, $assets);

    }
 private function uploadNewAsset($folder, string $path, $filename)
    {
        $filename = Assets::prepareAssetName($filename);

        $asset = new Asset();
        $asset->tempFilePath = $path;
        $asset->filename = $filename;
        $asset->newFolderId = $folder->id;
        $asset->setVolumeId($folder->volumeId);
        $asset->uploaderId = User::findOne()->id;
        $asset->avoidFilenameConflicts = true;
        $asset->setScenario(Asset::SCENARIO_CREATE);
        $res = Craft::$app->getElements()->saveElement($asset);

        if (!$res) {
            blogify_log("Failed to save image");
        }

        return $asset;
    }

Checked folderId and it matches. tempFilePath is {projectRoot}/storage/runtime/temp/{filename}, which also matches Craft CP request.

2
  • How are you defining the $path and $folder variables? The error might be with those.
    – MoritzLost
    Nov 19, 2021 at 8:40
  • @MoritzLost Added full context to question.
    – Matanya
    Nov 19, 2021 at 8:50

1 Answer 1

1

I am not sure yet what caused the issue, but it is probably somehow related to the fact that I also created the volume programmatically. At any rate, using an absolute path during volume creation solved the issue:

$volumesService->createVolume([
            'type' => Local::class,
            'name' => 'Assets Volume',
            'handle' => 'assetsVolumeHandle',
            'hasUrls' => 1,
            'url' => '/assets-url',
            'settings' => [
                'path' => 'assetsFolder'
            ]
        ]);

Replace assetsFolder with @webroot/assetsFolder (and while you're at it replace /assets-url with @web/assets-url).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.