- Issue created by @sir_squall
- 🇨🇭Switzerland sir_squall
Hi,
Ok for those who are also interrested, i have created a new module with a drush command, and here is the code that will do the migration :
<?php namespace Drupal\drush_migratecrops\Command; use Drush\Commands\DrushCommands; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseException; use Drupal\Core\Database\Connection; use Drupal\crop\Entity\Crop; /** * Drush command file. */ class MigrateCropsCommands extends DrushCommands { /** * Drush command that migrates the focal points * * @command migrate:crops * @aliases mcr * @usage migrate:crops */ public function migrate_crops() { $file_storage = \Drupal::service('entity_type.manager')->getStorage('file'); $crop_storage = \Drupal::service('entity_type.manager')->getStorage('crop'); // delete previous crops $crops = $crop_storage->loadByProperties(['entity_type' => 'file']); foreach ($crops as $crop) { $crop->delete(); } $database = Database::getConnection('default', 'migrate'); $count = $query = $database ->select('file_managed', 'fm') ->fields('fm', ['fid', 'uri','uuid']) ->fields('mc', ['y','x','height', 'width','style_name']); $query->leftJoin('manualcrop', 'mc', 'fm.fid = mc.fid'); $sandbox['last_fid'] = 0; $sandbox['num_processed'] = 0; $sandbox['num_skipped'] = 0; $sandbox['total_items'] = $count->countQuery()->execute()->fetchField(); $results= $query->execute()->fetchAll(); foreach($results as $result) { $style_name = $result->style_name; if(!$style_name) { continue; } // adapt old naming to new style naming switch ($style_name) { case 'image_liste' : $style_name = 'image_list'; break; case 'partenaire' : $style_name = 'partenaires'; break; case 'fond_liste' : $style_name = 'fond_list'; break; } // get FID on d10 $files =$file_storage->loadByProperties(['uri' => $result->uri]); $file = reset($files) ?: NULL; if (!is_null($file)) { $sandbox['last_fid'] = $file->id(); $crop_data = [ 'type' => $style_name, 'entity_id' => $file->id(), 'entity_type' => $file->getEntityTypeId(), 'uri' => $result->uri, 'x' => (int) round(($result->x ) + ($result->width / 2)), 'y' => (int) round(($result->y )+ ($result->height/ 2)), 'width' => $result->width, 'height' => $result->height ]; try { $crop = $crop_storage ->create($crop_data) ->save(); } catch (Exception $e) { $sandbox['num_skipped']++; $this->output()->writeln( 'unable to save: ' . $crop_data); } $sandbox['num_processed']++; } else { $sandbox['num_skipped']++; } } $this->output()->writeln( 'last_fid: ' . $sandbox['last_fid'] ); $this->output()->writeln( 'num_processed: ' . $sandbox['num_processed'] ); $this->output()->writeln( 'num_skipped: ' . $sandbox['num_skipped'] ); $this->output()->writeln( 'total_items: ' . $sandbox['total_items'] ); } }
- 🇪🇸Spain candelas
@sir_squall thanks for your module. I will try it for my migration.
Which module did you use for Drupal 10 to replace this one?
https://www.drupal.org/project/image_widget_crop → ?
Thanks - 🇨🇭Switzerland sir_squall
Hi,
Yes it's this one the image_widget_crop.
Thanks
- 🇪🇸Spain candelas
Tomorrow I will be testing it.
I have not made modules in drupal 10.
For what I see you use a migrate module, aren't you?
If you can give to me any tip, I would very happy :)
Thanks - 🇪🇸Spain candelas
@sir_squall THANKS!!!!
At the end I could work on it today.last_fid: 4404 num_processed: 1168 num_skipped: 0 total_items: 1676
I add here how I made it, just in case someone needs it (there are 13,097 sites using it)
I have drush 12.4.3.0 and I was getting
[ReflectionException (-1)] Class "\Drupal\drush_migratecrops\Commands\MigrateCropsCommands" does not exist
because I made an drush.services.yml
They changed how you make it
drush generate drush:command-file
and in the DrushMigratecropsCommands.php
<?php namespace Drupal\drush_migratecrops\Drush\Commands; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; use Drupal\Core\Utility\Token; use Drush\Attributes as CLI; use Drush\Commands\DrushCommands; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseException; use Drupal\Core\Database\Connection; use Drupal\crop\Entity\Crop; /** * A Drush commandfile. * * In addition to this file, you need a drush.services.yml * in root of your module, and a composer.json file that provides the name * of the services file to use. */ final class DrushMigratecropsCommands extends DrushCommands { /** * Constructs a DrushMigratecropsCommands object. */ public function __construct( private readonly Token $token, ) { parent::__construct(); } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('token'), ); } /** * Command description here. */ #[CLI\Command(name: 'drush_migratecrops:command-name', aliases: ['foo'])] #[CLI\Argument(name: 'arg1', description: 'Argument description.')] #[CLI\Option(name: 'option-name', description: 'Option description')] #[CLI\Usage(name: 'drush_migratecrops:command-name foo', description: 'Usage description')] public function commandName($arg1, $options = ['option-name' => 'default']) { $this->logger()->success(dt('Achievement unlocked.')); } /** * An example of the table output format. */ #[CLI\Command(name: 'drush_migratecrops:token', aliases: ['token'])] #[CLI\FieldLabels(labels: [ 'group' => 'Group', 'token' => 'Token', 'name' => 'Name' ])] #[CLI\DefaultTableFields(fields: ['group', 'token', 'name'])] #[CLI\FilterDefaultField(field: 'name')] public function token($options = ['format' => 'table']): RowsOfFields { $all = $this->token->getInfo(); foreach ($all['tokens'] as $group => $tokens) { foreach ($tokens as $key => $token) { $rows[] = [ 'group' => $group, 'token' => $key, 'name' => $token['name'], ]; } } return new RowsOfFields($rows); } /** * Drush command that migrates the focal points * * @command migrate:crops * @aliases mcr * @usage migrate:crops */ public function migrate_crops() { $file_storage = \Drupal::service('entity_type.manager')->getStorage('file'); $crop_storage = \Drupal::service('entity_type.manager')->getStorage('crop'); // delete previous crops $crops = $crop_storage->loadByProperties(['entity_type' => 'file']); foreach ($crops as $crop) { $crop->delete(); } $database = Database::getConnection('default', 'migrate'); $count = $query = $database ->select('file_managed', 'fm') ->fields('fm', ['fid', 'uri','uuid']) ->fields('mc', ['y','x','height', 'width','style_name']); $query->leftJoin('manualcrop', 'mc', 'fm.fid = mc.fid'); $sandbox['last_fid'] = 0; $sandbox['num_processed'] = 0; $sandbox['num_skipped'] = 0; $sandbox['total_items'] = $count->countQuery()->execute()->fetchField(); $results= $query->execute()->fetchAll(); foreach($results as $result) { $style_name = $result->style_name; if(!$style_name) { continue; } // get FID on d10 $files =$file_storage->loadByProperties(['uri' => $result->uri]); $file = reset($files) ?: NULL; if (!is_null($file)) { $sandbox['last_fid'] = $file->id(); $crop_data = [ 'type' => $style_name, 'entity_id' => $file->id(), 'entity_type' => $file->getEntityTypeId(), 'uri' => $result->uri, 'x' => (int) round(($result->x ) + ($result->width / 2)), 'y' => (int) round(($result->y )+ ($result->height/ 2)), 'width' => $result->width, 'height' => $result->height ]; try { $crop = $crop_storage ->create($crop_data) ->save(); } catch (Exception $e) { $sandbox['num_skipped']++; $this->output()->writeln( 'unable to save: ' . $crop_data); } $sandbox['num_processed']++; } else { $sandbox['num_skipped']++; } } $this->output()->writeln( 'last_fid: ' . $sandbox['last_fid'] ); $this->output()->writeln( 'num_processed: ' . $sandbox['num_processed'] ); $this->output()->writeln( 'num_skipped: ' . $sandbox['num_skipped'] ); $this->output()->writeln( 'total_items: ' . $sandbox['total_items'] ); } }
Have a very good day!!!!!
- Status changed to Needs review
11 months ago 3:44pm 16 January 2024 - 🇨🇭Switzerland sir_squall
De nada! Happy that I could help you on that! It took me a bit of time to develop that!
Have a nice day, next time I go to BCN you can offer me a beer ;)
- 🇪🇸Spain candelas
Of course, a beer and a tapa!
I live now in the country side, but if you come, I will go.
Now I realize that image_widget_crop doesn't have the manualcrop feature of reusing previous crops in images styles and I will collaborate to make a module to migrate to media_contextual_crop based on your module.
Just in case you are interested, https://www.drupal.org/project/media_contextual_crop/issues/3415186 💬 drush migrate command to port D7 Manual Crop module to Media Contextual Crop Active
See you in Barcelona :) - 🇮🇱Israel gdana
Hi, thank you @sir_squall for this module and also @candelas for the further instructions.
I've tried running this and have also encountered the message
Class "\Drupal\drush_migratecrops\Commands\MigrateCropsCommands" does not exist
@candelas, would you mind explaining what you've done other than generating the file via
drush generate drush:command-file
you've written that you've generated drush.services.yml by mistake. What does it mean? Did you create a composer.json file and drush.services.yml? and assuming you did, I'd really appreciate if you'd detail what is written in each file.
Thank you!
Dana - 🇮🇱Israel gdana
Sorry, nevermind, I managed to overcome these issues, now dealing with the actual code but will try and figure it out myself before asking dumb questions :)
- 🇫🇷France DrDam
Hi,
just an tiny proposal : the drush command may take a parameter/option as a key-map to manage style_name changes
- 🇮🇱Israel gdana
Hi, thanks. Would it be possible to detail the required steps including order?
What I did was migrate the site/content (thousands of nodes with images), before trying out this module. They were migrated successfully, including field definitions and corresponding images.
Then I used this migrate module and eventually seemed to run it successfully, although without parameters. I can see the crop table in the database with the correct width, height and fids (and also the calculations regarding X and Y - are these adjustments compatible in all cases...?).
After all this I found out that all of the image styles the use cropping were not migrated in the initial site migration so I defined one as a first test, enabled image_widget_crop, and defined the field that uses the image style to use image_widget_crop.
It didn't work... am I missing something? I can see the connection in the table between files and crop parameters but nothing connecting to specific fields or nodes. Should I change the order of actions? missing some step altogether?It would be very helpful if you can add some instructions for the module...
Thank you! - 🇨🇭Switzerland sir_squall
Hi,
You have to do the standard migration process, once you have your new website with the good image styles, the content migrated and the image migrated, then you can run this little script, if you have renamed the images style, you can also adapt the code, as I did:
// adapt old naming to new style naming
switch ($style_name) {
case 'image_liste' : //OLD ONE
$style_name = 'image_list'; // NEW ONE
break;
case 'partenaire' : //OLD ONE
$style_name = 'partenaires'; //NEW ONE
break;
case 'fond_liste' : //OLD ONE
$style_name = 'fond_list'; //NEWONE
break;
}Then you run the command and the crops will be imported successfully, you don't have to take care of the x y, the calculation is the good one.
Thanks
- 🇮🇱Israel gdana
Great, thank you. I will try the whole thing again on a clean slate.