Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Wondering why there is no corresponding module_get_weight() for module_set_weight(). The typical use case is to set my module's weight to one higher than another module, but I don't necessarily know what the weight of the other module is:
$weight = (int) db_query("SELECT weight FROM {system} WHERE name = 'dependency'")->fetchField(); db_update('system') ->fields(array('weight' => $weight + 1)) ->condition('name', 'mymodule') ->execute();
I don't see a way to possibly do this with the newly-converted code.
Comment | File | Size | Author |
---|---|---|---|
#37 | 1808132-37.patch | 13.08 KB | TR |
#34 | 1808132-34.patch | 13.08 KB | TR |
#32 | 1808132-32.patch | 12.05 KB | TR |
#21 | complement-1808132-20.patch | 1.04 KB | markdorison |
#15 | complement-1808132-15.patch | 1.05 KB | alansaviolobo |
Comments
Comment #1
plachNot sure about the 0 default. A module that does not appear among enabled or disabled modules simply does not exist: FALSE or an exception would make more sense to me.
Also, the ifs are not very readable right now.
Comment #2
plachComment #3
sun1) We could also return NULL or FALSE. In that case, we should ensure that module_set_weight() converts both NULL and FALSE into 0 though.
2) I'm generally not a fan of reversed "yoda" conditions either, but there is a single case in which they make sense: the one in this patch; i.e., a variable assignment combined with a value comparison, leveraging the right-to-left processing of the PHP interpreter.
Comment #4
Crell CreditAttribution: Crell commentedCan we just wait and add this as a method to #1331486: Move module_invoke_*() and friends to an Extensions class? We really need to stop adding utility functions to the system and start adding well-thought-out methods and APIs.
Comment #5
sunSorry, but the time for "waiting" is over. ;)
Comment #6
Crell CreditAttribution: Crell commentedSo is the time for continuing "throw a function at the wall and see if it sticks" development... At minimum, such a function should be marked as @deprecated with a note to replace it before release. (And the same for set_weight()).
Comment #7
webchickWhat? I don't agree with that at all. If/when #1331486: Move module_invoke_*() and friends to an Extensions class actually happens, it's free to deprecate whatever it likes. Until then, this is the API we actually have, not the one we wish we had.
Comment #8
plachThat's exactly why I think a FALSE/NULL/exception (probably the latter) would make more sense:
module_set_weight()
cannot assume that what's returned bymodule_get_weight()
is always valid. An empty catch statement would be probably a good way to handle the OP scenario when the "depended" module is not installed.I'm not talking about yoda conditions, I'm talking about yoda conditions + an assignment in the same if test. Why?
Comment #9
plachWhat about this?
Comment #10
sunThrowing an exception looks overboard to me. Returning FALSE should be sufficient.
Compare:
vs.
Comment #11
plachWell, the typical use case for this is when your module depends on another one, in this case
module_get_weight()
will always return a valid weight. Now that we are starting to really use exceptions IMHO we should try and get rid of those icky multiple-typed results. Ifmodule_get_weight()
cannot return a weight, making it return NULL or FALSE is just poorman's exception handling.Comment #12
plachComparing:
vs.
The former is more verbose but also more readable to my eyes, as its code is cleaner and not "infested" with ifs and ternaries everywhere (actualy I'm thinking about slightly more complex cases, where more than one exception needs to be handled).
Comment #13
leschekfm CreditAttribution: leschekfm commentedI would agree with plach.
In my opinion, if a function cannot return a valid value which can be used directly after the function call, it should throw an exception. I think the practice of returning 'special' values to determine whether there is a valid result was one of the reasons to introduce exceptions.
Just my two cents
Comment #14
jibran9: drupal-module_get_weight-1808132-9.patch queued for re-testing.
Comment #15
alansaviolobo CreditAttribution: alansaviolobo commentedcould use some help with this.
Comment #16
Crell CreditAttribution: Crell commentedIn the 2 years since this issue was last active, module functionality has almost entirely moved to a service object. There's no need for another function, but it may make sense to put on the appropriate service object.
Comment #17
alansaviolobo CreditAttribution: alansaviolobo commentedI was wondering why the set method is still around. is there another issue to move it to a service object?
Comment #18
tim.plunkettThat code has changed very drastically.
system.module
is nowcore.extension
, there is no disabled modules, etc.Comment #20
markdorisonComment #21
markdorisonUpdated to use core.extension.
Comment #24
TR CreditAttribution: TR commentedSo ... the {system} table was removed more than 5 years ago. Change record: https://www.drupal.org/node/1813642
With it disappeared the ability to set module weights relative to other modules, needed in order to ensure a relative loading order so that dependencies may be met.
This is not a "feature request", it is a bug - functionality has been removed. The change record promised a module_get_weight() function, but that never materialized and the patch in #21 doesn't work because ModuleHandler::moduleExists() no longer returns an integer weight.
As far as I can tell, the concept of module weight is still used internally, we just don't have access to it any more. And a module_set_weight() without a module_get_weight() is pretty useless - the only reason to use module_set_weight() is to set a weight RELATIVE to another module, so you have to be able to find out the weight of that other module.
Is there some alternative solution I don't know about? Perhaps the relative weights are guaranteed by the dependency order listed in the .info.yml files? What about for use cases where there is no dependency involved but if a specific other module exists we still need to load after that module?
If weight is a concept that's no longer relevant to contrib development, then module_set_weight() should be removed and this issue should be changed to "won't fix". Then the above change record should be modified and there should be some documentation added which explains how module relative weights are handled in D8.
I've changed this to "Major" because this is missing functionality which is needed in order to port some modules to D8.
Comment #25
tim.plunkettIdk about the second hunk of that patch. But the first hunk is fine. If the module is installed, it's weight will be in that config
Not going to play status wars, because just fixing this is a better use of time
Comment #26
TR CreditAttribution: TR commentedIf all the module weights are now stored in the core.extension config, then how about something like the following:
1) Add
setWeight($module, $weight)
andgetWeight($module)
methods toModuleHandler
2) Deprecate
module_set_weight()
This puts the weight functionality in the logical and expected place, removes the arbitrary procedural function, and importantly restores the D7 functionality of being able to set and get weights.
Let's see if this works ...
Comment #28
TR CreditAttribution: TR commentedCorrected the missing public on the two new methods.
Comment #30
TR CreditAttribution: TR commentedThat's an unhelpful message ...
I spun up a new site to test this and the real error is:
So basically I can't inject the config.factory service into the module_handler because module_handler is already being injected into config.factory.
So what's the Drupal way out of this? Static call to config()? Injection of container then a lazy get of the config.factory?
I'll work on this some more if there is some consensus for the approach I outlined in #26.
Comment #31
tim.plunkettThe ModuleInstaller has to use dedicated calls to
\Drupal::configFactory()
for the same exact file:I think #26 sounds great
Comment #32
TR CreditAttribution: TR commentedComment #33
dawehnerShould't the old code now call to
\Drupal::moduleHandler()->setWeight
?In case we can't find the module, should we throw an exception?
Comment #34
TR CreditAttribution: TR commentedGood point, yes, here's a new patch with the old code removed and replaced by a call to
\Drupal::moduleHandler()->setWeight()
I'm not sure exactly what you mean here, because the code you quoted isn't part of the patch ... But if you're referring to the 5-year old discussion above, then I think that is outdated.
No public methods on ModuleHandler throw exceptions currently. ModuleHandler::loadInclude(), for instance, does not throw an exception if the include file is not found ... If we throw an exception in setWeight()/getWeight() they would be the only ones. IMO that makes adding exceptions here a little outside the scope of the patch, which I see as restoring the missing getWeight() functionality.
So I vote no on throwing an exception in either setWeight() or getWeight(). If added, they would be the ONLY public functions on ModuleHandler that throw exceptions. Maybe there should be a separate issue opened to re-architect ModuleHandler to use exception handling. But if the consensus is that we should just do it for these two new methods, I'll add that to the patch. I have no desire to stall the issue on this point.
...
All the use cases for setting a weight that I can think of involve relative weights, with the code knowing exactly which other module(s) are involved. (Which is why getWeight() is critical to have!) Why else would you arbitrarily set your own module weight? I don't know. And why would you ever want to set another module's weight?
So say there's another module you conflict with because you both hook_form_alter() the same form so you have to ensure order of execution between the two modules. Then:
1) If you depend on that module, the dependency will ensure the other module exists, so the pattern would be:
2) If you don't depend on that module, you at least know the name of the module so the pattern would be:
Note we already have a reference to $module_handler if we need to set the weight, so checking the existence of the other module is trivial.
Comment #36
AaronBauman+1 for adding getWeight() and the use case of relative weights.
#34 worked for me
Minor nits since 8.5.0 is released now, needs updated docs and re-roll against 8.6:
Comment #37
TR CreditAttribution: TR commentedChanged 8.5.0 to 8.6.0 in two places, no other changes.
@aaronbauman: Maybe you can RTBC this if there's nothing else?
Comment #38
AaronBaumanI didn't see in the thread: do we not need additional tests for this?
+1 RTBC from me, but I'm jumping in late so I'll let someone else switch the status.
Comment #39
TR CreditAttribution: TR commentedThe "Needs tests" tag was added in #18 because none of the proposed patches before then had test cases.
The patch in #37 (and previous patches back to and including #32) DO have test cases which test both setWeight() and getWeight(),so the tag is no longer relevant.
Comment #40
dawehnerIt feels like moving this to the Module Installer seems to better bet.
Comment #41
TR CreditAttribution: TR commentedI think it should be in the ModuleHandler for several reasons:
The ModuleInstaller has a very specific functionality - it *only* installs and uninstalls modules. Period. ModuleInstallerInterface:: defines these methods, and nothing else:
ModuleInstaller does NOT deal with any metadata about the modules being used - to find out what modules are installed or anything else about modules, we currently use the ModuleHandler.
Some of the many methods of ModuleHandlerInterface::
Additionally, the use case I presented in #34, which I believe to be the primary way getWeight()/setWeight() will be used, is this:
This use case needs to call ModuleHandler::moduleExists(). If setWeight()/getWeight() are in the same class as moduleExists(), we only need the reference to ModuleHandler to perform this task. If they are in ModuleInstaller instead, then you need a reference to both ModuleHandler and ModuleInstaller to perform this task. This tells me that setWeight()/getWeight() are more related to the existing ModuleHandler functionality.
If there is some consensus for putting this in ModuleInstaller, I will rewrite the patch. But for now I think the correct place to put this is in ModuleHandler.
Comment #42
dawehnerThe reason why I suggested to move it to the module installer are the following:
I hope we are able to drop this feature in the module. It is a weird edge case feature.
Note: The module installer also didn't use to exist before.
Comment #43
Prashant.c@TR
I tested the
\Drupal::moduleHandler()->setWeight($module_name, $weight);
in one of my modules and it works well.Great work.
Comment #44
alexpottI'm in favour of working on deprecating the entire concept of module weight. I think module weight should be an internal detail between itself and the module system. Ideally with autoloading the concept of module weight can eventually be deprecated. If any code is actually relying on this then it really is a code smell. We should declare dependencies properly and order the module list by that instead. And if you still have something that is not solved by that then you have
hook_module_implements_alter()
Yes the above might only be possible in D9 but I don't think that means we should "improve" the API now. We should try to put everything in place to remove it.
If we must do this then...
Let's not add API which is only used in test of itself. The fact we've got away without a getter for five years shows us something.
Comment #45
alexpottI've opened #2968232: Deprecate module_set_weight() to see how far we can get with Drupal 8 for #44.
Comment #46
alexpottIf we're going to refactor the module weighting system I think we should change setting and getting of weight to be a config activity. So for example to get a weight you do:
and to set a weight we do
The actually sorting of the module list according to weight can go into wherever it needs to. ModuleInstaller / ModuleHandler / Kernel. Ideally they would be a config listener that would do the module sorting for the module handler etc... if module weights changed and nothing was installed or uninstalled (as those cases need to be handled by the ModuleInstaller). This would mean that editing the core.extension config via Drush or the Config single import / export would have a chance to actually work as expected.
Comment #54
andypostneeds work for 9.4.x and proper message
Comment #55
bobburns CreditAttribution: bobburns commentedI realize this thread is old - somewhat - but in the real world for two upgraded modules - uc_addresses and uc_extra_fields_pane on Drupal 8.9.20 the function at the head of this post exists in the Drupal 7 install files. So I used
$weight = \Drupal::moduleHandler()->getWeight('uc_addresses', '=');
\Drupal::moduleHandler()->setWeight($weight, + 10);
for uc_extra_fields_pane and at first it worked then on uninstall threw an unsupported method error and I had to change this into
$weight = \Drupal::moduleHandler()->schema()->getWeight('uc_addresses', '=');
\Drupal::moduleHandler()->schema()->setWeight($weight, + 10);
to uninstall and install it again the schema()-> caused an error and had to be removed. It does the same thing in uc_addresses install which is instead
$weight = \Drupal::moduleHandler()->getWeight('uc_quotes', '=');
\Drupal::moduleHandler()->setWeight($weight, + 1);
Am I doing something wrong, or are there thoughts on how this is now to be used ??
(I am still on 8.9.20 to use the Upgrade Status module with Upgrade Rector to solve all module issues of the entire site before upgrading so it will work on Drupal 9 as I have a Drupal 9 site I can switch to by changing the directory name with the same database and it was found the Drupal 9 site would park on an error if certain modules were not fixed because an API call was depreciated AND removed in Drupal 9 so effectively hosing the site)
Comment #56
andypostATM there's modules extension list as primary source of truth so I think it should be closed in favour of #2968232: Deprecate module_set_weight()
The reason is that most of time modules need to order specific hooks but not the modules' list
Makes sense to transfer credits to new issue and close this one
Moreover module list should be moved out of handler
- #2941155: ModuleHandler should not maintain list of installed modules now that ModuleExtensionList exists
- #3179546: Tag ExtensionList services with extension.list