? Pending

User tests: Successful: Unsuccessful:

avatar laoneo
laoneo
6 Mar 2017

Summary of Changes

This is a first approach to namespace com_content with the current architecture. It is fully BC and will support extensions running on J3 with none namespaced classes.

Namespace

On the back end, com_content has the following namespace, according to the document from Odense:
Joomla\Component\Content\Admin

Dispatcher

The single entry file administrator/components/com_content/content.php was deleted and replaced with a dispatcher file. The dispatcher itself was changed to load the correct controller according to the task, like that the JControllerLegacy::getInstance() function is only used when the component has the legacy set up.

Controllers

All controllers are in the Controller folder and are the same classes as in the none namespaced version. The only exception is the ContentController which has now the name Content.

Models

The models are moved files from the models folder to Model.

Tables

The tables are moved files from the tables folder to Table. Additionally there is a new table Article which extends from JTableContent because I think JTableContent should not exist and on a later stage being moved to Joomla\Component\Content\Admin\Table\Article.

Views

Every view does have it's own namespace and for every format there is a single class. This is again according to the decisions from Odense.

Resources

Fields, forms and layout files are moved to the resources folders. Like that we have a clear separation between classes and resources. I was unsure if fields should get also their own namespace or if it should be treated as a resource, so I put them into resources.
This will be done in a new pr as not everybody agrees with that approach.

Install

As something is resetting the namespace value in the extensions table you need to manually run the following query after installation:

UPDATE  `sites_cms`.`j_extensions` SET  `namespace` =  'Joomla\\Component\\Content' WHERE  `j_extensions`.`extension_id` =22;

@mbabker any clue what?

Testing Instructions

Open Content -> Articles in the back end and play around with it.

Expected result

Same behavior as with the none namespaced ones.

avatar laoneo laoneo - open - 6 Mar 2017
avatar laoneo laoneo - change - 6 Mar 2017
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 6 Mar 2017
Category Administration com_content
avatar franz-wohlkoenig
franz-wohlkoenig - comment - 6 Mar 2017

open Article, got:
bildschirmfoto 2017-03-06 um 09 24 56
After deinstalling PR and clear Cache got:
bildschirmfoto 2017-03-06 um 09 26 26

avatar laoneo
laoneo - comment - 6 Mar 2017

Did you test it against the Joomla 4 branch?

avatar franz-wohlkoenig
franz-wohlkoenig - comment - 6 Mar 2017

No, haven't noticed its about 4.

avatar laoneo laoneo - change - 6 Mar 2017
Title
[RFC] Namespace com_content
[RFC] [j4] Namespace com_content
avatar laoneo laoneo - edited - 6 Mar 2017
avatar piotr-cz
piotr-cz - comment - 6 Mar 2017

Did you perhaps consider using aliases in some compat layer?

class_alias('Joomla\Component\Content\Admin\Controller\Article', 'ContentControllerArticle');
avatar laoneo
laoneo - comment - 6 Mar 2017

If they are needed, then for sure we will add them to the classmap file.

avatar piotr-cz
piotr-cz - comment - 6 Mar 2017

I'm asking because 3p extensions like modules similar to mod_article_latest may be using com_content classes

avatar joomdonation
joomdonation - comment - 6 Mar 2017

Since I work on a similar thing in PR #14258, I would like to make a comparison between the two PRs (only related to MVC layer only) base on a quick look at your PR code:

  1. Your PR doesn't prepare for HMVC support
  • Both dispatcher and controller is coupled with application input. It is not possible to pass the Input object from outside
  • JPATH_COMPONENT is still being used in the new code
  1. The code of method createModel (and createView) is coupled with the current controller namespace, so it is not possible to get a backend model from frontend controller and vice versa . You also assume that the namespace model and view class always exist (it is just RFC, I know)

  2. In some place, you need to run code to get the component name. I think we can safely store $option as a property of the controller - get from controller input - and pass it to model, view while creating, that would help detecting the component easier.

  3. Just a minor thing, your structure requires change to the JComponentHelper to support two kinds of component end point file:

  • dispatcher.php
  • content.php

With my propose, no change needed to JComponentHelper. We still have one file content.php with this code
(new Joomla\Cms\Dispatcher\Dispatcher())->dispatch();

  1. About the location of layout files, I think just keeping it inside the View folder might be better:
  • People already familiar with it, so they will know where to find the layout file to create override
  • We don't have to change code of the Template manager to look up for new location to create layout override

To be more clear, my only concern is whether we want to support HMVC in our namespace MVC or not. If not, this PR is fine (some minor things can be addressed easily) and I will stop working on what I am working on.

avatar mbabker
mbabker - comment - 6 Mar 2017

Your PR doesn't prepare for HMVC support

That doesn't belong as part of this PR. This is simply working out details for component class namespacing.

avatar laoneo
laoneo - comment - 6 Mar 2017

@joomdonation:

  1. In London, we skipped HMVC as the only thing we really need are reusable layouts. For what use case do you need HMVC in Joomla?
  2. It is still possible, just overwrite the createModel function in your controller. What can be changed is to use the second parameter as client. This would mean then that in namespaced context it defines if the model from site or admin should be loaded.
  3. Can be, like it is in this pr it is similar to what we have now in j3, don't want to clutter this pr with optimisation as it is already big enough. But there is still room for improvements. Honestly, for me it is ok to have it as it is as it should be clear from the model itself to which component it belongs to.
  4. Dispatching the component is IMO the job of the application. The application should execute the component dispatcher which does set up the controller which get's from the application input the state and does set up the model to get the data from for the view.
  5. This is a similar approach like it is in symfony, where they store the twig files in the resources folder.
avatar joomdonation
joomdonation - comment - 6 Mar 2017

@laoneo

  1. Sometime, we need to display a view of a component inside a module or inside a Joomla article, that's why we need HMVC support (the requirement is real, that's why there is extension like this developed https://www.regularlabs.com/extensions/componentsanywhere )

Also, it will help we develop everything inside component, the module is actually show a view in a component, like this https://github.com/akeeba/akeebasubs/blob/development/modules/site/akmysubs/mod_akmysubs.php#L41

  1. We can implement support for it easily without having to create override, just call something like this

$model = $this->getModel('article', '\Joomla\Component\Admin\Model')

article is name of model, while \Joomla\Component\Admin\Model is considered as prefix in getModel method of the controller

  1. As I said, just a minor thing

  2. I haven't developed anything using symfony, just concern about how people will create template override for components.

avatar laoneo
laoneo - comment - 6 Mar 2017
  1. That's what I tough, you are talking only about views. So it would be more clear when we use reusable layouts instead of the whole MVC stack. @yvesh made a pr #13684 about that topic. I'm more in favor of that then running a whole MVC stack just to get an article view.
  2. IMO it should be more like $model = $this->getModel('Article', 'Site');.
  3. Template overrides will be in the same location as before.
avatar joomdonation
joomdonation - comment - 6 Mar 2017

#1. The approach in #13684 seems doesn't get positive feedback, I can see at least two developers disagree with it. Also, for HMVC support, I think @wilsonge supports it, see his comment #14258 (comment) (item 2 in the comment)

#2. That would work, too.

#3. I know. It is just where to find the original layout to create override. Right now, we support two places:
views/ViewName/tmpl
View/ViewName/tmpl
With your resources folder, we will end of with third location (and it is still correct that you will need to edit Template Manager code to support your new location)

avatar mbabker
mbabker - comment - 6 Mar 2017

One of the problems in #13684 IMO is that it basically turns a view agnostic JViewLegacy into an HTML first view class. We actually need to subclass it and have format specific subclasses (similar to https://github.com/joomla-framework/view/tree/2.0-dev/src). It's not so much the approach is bad, but architecturally it's yet another move toward the CMS being so heavily HTML first (only) that it identifies a need to rethink things.

avatar laoneo
laoneo - comment - 6 Mar 2017
  1. True, wasn't thinking about this.
avatar laoneo
laoneo - comment - 6 Mar 2017
  1. Honestly for this pr it is not really relevant how the dispatcher works, it is more about the general set up of a component. So when this one is in a state where it can get merged I suggest then to focus on your implementation of the dispatcher. Otherwise we are discussing the same thing in multiple pr's.
avatar mbabker
mbabker - comment - 6 Mar 2017

So just talking about right now, this PR as is and what is currently merged into 3.8/4.0, for me we're still missing some autowiring stuff.

Anyone using a component still has to know its internals (namespaces, file paths, etc.) and still manually register stuff into the system for it to be used. Somehow I'd like to see things evolve in a way where a component's internals just automagically get autowired (think a FOF3 component when you fetch its container and how part of that process handles the autoloader configurations).

I know there's going to be at least one response that's going to be "well, that's the job of the dispatcher". I don't see the dispatcher having that much responsibility when it comes to components, that seems like we're cramming too much responsibility into the dispatcher class (think SRP, the name to me implies that the dispatcher's role is to dispatch a component task, it shouldn't get too complex with too many responsibilities; classes are cheap). I'd still like to be able to direct load and use a component's model in a module if desired versus the code being forced to use a dispatcher to perform a task or even instantiate a dummy dispatcher just to do the wiring.

I'm about to go do some other work, but wanted to throw that thought out there. It's probably out of scope for this PR as is but it is definitely an issue that's going to need to be addressed.

avatar laoneo
laoneo - comment - 6 Mar 2017

component's internals just automagically get autowired

That would be nice, but not by loading from the database or by parsing a manifest file.

avatar mbabker
mbabker - comment - 6 Mar 2017

What's wrong with either of those? We already load component extension records into memory on every request, so at worst we're adding some additional load onto that query and the resulting cache data.

avatar laoneo
laoneo - comment - 6 Mar 2017

I'm just not a fan of loading more than what is needed. If a cache gets created somehow which contains all the namespaces, then I'm fine with that. But loading all extensions from the database or parsing all manifest files on every page request is for me too much load and not acceptable.

avatar yvesh
yvesh - comment - 6 Mar 2017

@laoneo agree, but than we need something like the symfony production environment (which needs refresh for every change). But getting your point that loading 100s extensions namespaces for every request needs solution (if i understood you correctly)

avatar mbabker
mbabker - comment - 6 Mar 2017

Well, through a request cycle the parameters for several components end up getting used, so we're already loading all components into memory in JComponentHelper and caching that query result since the method gets hit on every request (off hand the system includes lookups for the guest user group from com_users and the default language from com_languages, I'm not sure if there are other components that have "global" configuration options like that).

Plugins have a similar query loader and the result is cached by a key generated based on the active user's groups (since that would affect what plugins are loaded/triggered during a request cycle).

JModuleHelper also has a cache for the module list query, though that one has some additional filters for each request (i.e. the active menu item, user groups, publish up/down, etc.).

So, a good chunk of the #__extensions and #__modules tables are already getting read into memory during a request cycle, and cached when that is enabled so it's persisting.

Also, we don't HAVE to read all the data into memory immediately. As much of the system that can be lazy loaded should be, including lazy conversion of parameters to Registry instances as I've done with a couple parts of the API. If the dispatcher were used to do all this automagic stuff, the namespaces should only be added to the autoloader when something explicitly requests it. If that data source were the component manifest, or a separate configuration file similar to FOF, that would be the point the source is processed (granted if the source were the manifest, we're already creating a cache of that in the database, so we would just include that in the query result (and inherently cache) for at least the component and plugin helpers and it would just be another property on the resulting objects).

I don't think we can get to a point where Joomla can create a dynamic configuration source like the compiled appProdProjectContainer.php in Symfony projects. But that would be another optimal option that has all the mapping info (this would equate to the %kernel.bundles_metadata% parameter in the container that has the bundle name, namespace, filesystem path, and if it has a parent).

avatar laoneo
laoneo - comment - 7 Mar 2017

If that's really the case that all extensions do get loaded anyway, then it can probably work out to have the name in the database.
For me it is important to have an agreement on the structure of a namespaced extension. If we can agree that this is the case with this PR. Then I would like to get it merged. From there I can start a PR autoloading the namespaces from the database, otherwise this one will get too big.

avatar joomdonation
joomdonation - comment - 7 Mar 2017

If the decision is just for structure of a namespace a component, I agree with most of what you did here. To me, there are few items left and the Joomla 4 team will have to make decision:

  • Whether we need to introduce resources folder or still keep these items inside Model and View folder like before
  • The View controller, maybe it would be better calling is as Display controller
  • For the code in controller which create View and Model object, you assume that these classes always exists, need to check and throws exception if these class doesn't exist

Then we can get testers to test it. If it works, it can be merged so that you can namespace frontend part of the component (and other components).

The technical discussion of whole MVC layer, not sure when we have a final decision and it could hold the namespace effort back.

avatar yvesh
yvesh - comment - 7 Mar 2017

@joomdonation All things that needs to be discussed, but let's do one step after another. We have the akward habit to rush things in without a greater concept, so better to have multiple iterations than one big "final" step.

avatar joomdonation
joomdonation - comment - 7 Mar 2017

@yvesh Maybe I didn't explain it properly but I just wanted to give my feedback about the structure of namespace com_content, I didn't say (or didn't want to say) that we have to make all decisions at once.

avatar laoneo
laoneo - comment - 7 Mar 2017

For the code in controller which create View and Model object, you assume that these classes always exists

Why? I think I haven't done something special in that PR which should force them? If this is the case, then it should be removed.

avatar joomdonation
joomdonation - comment - 7 Mar 2017

@laoneo See my comments above at the code which creates Model and View class.

avatar mbabker
mbabker - comment - 7 Mar 2017

Whether we need to introduce resources folder or still keep these items inside Model and View folder like before

If you're familiar with Symfony bundles, you know that they use a Resources folder as a location to store all sorts of miscellaneous files (translations, service configurations, validator configurations, Doctrine entity mappings, and views (our layouts)). From that aspect, I think it'd be better to start consolidating "random" things into one spot. It seems like a better convention to keep the PHP class files in one path and not continue to spread miscellaneous files all throughout the system by grouping them into one path.

avatar laoneo laoneo - change - 7 Mar 2017
Labels Added: ?
avatar laoneo
laoneo - comment - 7 Mar 2017

@joomdonation added checks if the class do exist. On the second commit I'v changed the code to consider the prefix, which can be either Site or Admin.

@mbabker so you agree with the resources folder approach or not? Sorry but your last sentence was for me as a none native English speaking person just not understandable.

avatar mbabker
mbabker - comment - 7 Mar 2017

Yes we should do it. Seems better to put all the resources into one resources folder than spread them all over a component.

avatar laoneo
laoneo - comment - 7 Mar 2017

What about the rest, some feedback about them?

avatar joomdonation
joomdonation - comment - 7 Mar 2017

@laoneo Just a very quick look at your new code, I am afraid of it still not correct;

  • In case model doesn't exists, you should return false instead of return null. That's because the getModel method in controller expect to receive a Model object or Boolean value

  • The createModel method is now support getting both from frontend and backend model. Unfortunately, with your dispatcher code (have to read the JLoader::registerNamespace method to confirm), you only register the current path (JPATH_COMPONENT), so I guess in frontend, you cannot load a model from backend and vice versa. To be more clear, when you are in frontend, the backend path is not registered with auto-loader, so backend model could class is not auto-loaded

  • The code for getting frontend and backend namespace looks abit long and difficult to read to me. Would it be better if we calculate base namespace of the component (Joomla\Component\Content in this case), store it as a property of the class. Then getting frontend and backend namespace of model would be easier:

$this->namespace.'\Admin\Model\' or $this->namespace.'\Site\Model\'

That's the approach I use on my PR, see the code https://github.com/joomdonation/joomla-cms/blob/4295a3c310f8b9b41856d2c495621a095a3c4809/libraries/src/Cms/Controller/Controller.php#L1230-L1249

avatar laoneo
laoneo - comment - 7 Mar 2017

@joomdonation:

  1. Fixed in last commit.
  2. Registering namespaces should be discussed in it's own pr. According to Michaels comment it will be probably possible to load all namespaces of all extensions, so both Admin and Site namespaces are loaded anyway at that point.
  3. I'm not a fan of redundant data, when we have the information in the namespace already.
avatar joomdonation
joomdonation - comment - 8 Mar 2017

@laoneo

  1. Yes, that's redundant data. However, IMO, it's better to have it:
  • Calculate it only one time in class contructor.
  • Use it whenever you need instead of calling $reflect = new \ReflectionClass($this); $reflect->getNamespace() everywhere in the class
  • The code for getting model and class name (base on Admin , Site prefix) is clearer than what you are having now.
avatar laoneo
laoneo - comment - 8 Mar 2017

Honestly I don't see a reason to put the namespace as prefix, I mean then you can create the instance directly instead of creating the model trough another function.

avatar joomdonation
joomdonation - comment - 11 Mar 2017

Honestly I don't see a reason to put the namespace as prefix, I mean then you can create the instance directly instead of creating the model trough another function.

I was thinking about different thing (wrong BTW). Also, look at the current implementation, I even not sure if we should support Site/Admin as the prefix because as you said, we can create model class directly when needed. We will wait for feedback from others.

avatar laoneo
laoneo - comment - 11 Mar 2017

More and more I think the same, we should create the model instances directly in the code and not trough the createModel function. Then it is typed and makes the code more readable. When all classes are loaded anyway.

avatar joomdonation
joomdonation - comment - 12 Mar 2017

The view_list property in Admin controller is also not being detected properly. After publishing an article, we are being redirected to administrator/index.php?option=com_content&view=\articles (note the backslash before article).

Only found two issues which I just reported while doing quick test today

avatar laoneo
laoneo - comment - 12 Mar 2017

Fixed the two errors.

avatar laoneo
laoneo - comment - 16 Mar 2017

@wilsonge should we not merge this one that @joomdonation can work out his pr's on a live example? I guess we all agree on the set up, if this one get merged then we can switch focus to the Dispatcher and MVC issues. Like that it will be easier to understand how the changes by @joomdonation should work.

avatar yvesh
yvesh - comment - 16 Mar 2017

@laoneo can you fix the merge conflicts?

avatar laoneo
laoneo - comment - 16 Mar 2017

Will do when it will be merged, otherwise I have to do that multiple times.

avatar laoneo
laoneo - comment - 22 Mar 2017

Just for the record, changed the code to the new dispatcher made by @yvesh.

avatar laoneo
laoneo - comment - 7 Apr 2017

@joomdonation is taking over the namespacing part so I'm out.

avatar laoneo laoneo - change - 7 Apr 2017
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2017-04-07 07:24:17
Closed_By laoneo
avatar laoneo laoneo - close - 7 Apr 2017
avatar laoneo laoneo - change - 7 Apr 2017
Status Closed New
Closed_Date 2017-04-07 07:24:17
Closed_By laoneo
avatar laoneo laoneo - change - 7 Apr 2017
Status New Pending
avatar laoneo laoneo - reopen - 7 Apr 2017
avatar joomla-cms-bot joomla-cms-bot - change - 7 Apr 2017
Category Administration com_content Administration com_content Modules Libraries
avatar laoneo
laoneo - comment - 8 Apr 2017

I made some changes that the factory, app and input are passed to the parent controllers.

avatar joomdonation
joomdonation - comment - 8 Apr 2017

I installed a copy of https://github.com/digital-Peak/joomla-cms/tree/j4/namespace/component/content , and there are some errors;

  1. Look like there is no changed to extensions to set namespace data for com_content yet

  2. I inserted the data manually to that table. Then for some reasons, cpanel still broken Fatal error: Class 'Joomla\Component\Content\Administrator\Helper\ContentHelper' not found in E:\www\namespacecontent\administrator\components\com_content\helpers\content.php on line 19

  3. I tried to access to http://localhost/namespacecontent/administrator/index.php?option=com_content and saw error 500 - An error has occurred.

Layout default not found.

Return to Control Panel

Look like it doesn't work yet (or there is error from my end). Could you please check?

avatar joomdonation
joomdonation - comment - 8 Apr 2017

In frontend, we are having errors, too.

Fatal error: Class 'Joomla\Component\Content\Administrator\Helper\ContentHelper' not found in E:\www\namespacecontent\administrator\components\com_content\helpers\content.php on line 19

Time Memory Function Location

1 0.0191 364168 {main}( ) ...\index.php:0
2 0.0292 370120 require_once( 'E:\www\namespacecontent\includes\app.php' ) ...\index.php:30
3 0.3974 3024424 Joomla\CMS\Application\CmsApplication->execute( ) ...\app.php:35
4 0.7428 4753320 Joomla\CMS\Application\SiteApplication->doExecute( ) ...\CmsApplication.php:393
5 0.8803 5621680 Joomla\CMS\Application\SiteApplication->dispatch( ) ...\SiteApplication.php:233
6 0.9671 5857848 Joomla\CMS\Component\ComponentHelper::renderComponent( ) ...\SiteApplication.php:194
7 0.9722 5864112 Joomla\CMS\Component\ComponentHelper::executeComponent( ) ...\ComponentHelper.php:398
8 0.9759 5893640 require_once( 'E:\www\namespacecontent\components\com_content\content.php' ) ...\ComponentHelper.php:424
9 1.0092 6120880 Joomla\CMS\Controller\Controller->execute( ) ...\content.php:39
10 1.0092 6120880 ContentController->display( ) ...\Controller.php:710
11 1.4061 6801232 Joomla\CMS\Controller\Controller->display( ) ...\controller.php:113
12 1.4363 6943704 ContentViewArticle->display( ) ...\Controller.php:672
13 1.6681 7753024 Joomla\CMS\Application\WebApplication->triggerEvent( ) ...\view.html.php:221
14 1.6682 7753104 Joomla\Event\Dispatcher->dispatch( ) ...\EventAware.php:109
15 1.6686 7753576 call_user_func:{E:\www\namespacecontent\libraries\vendor\joomla\event\src\Dispatcher.php:402} ( ) ...\Dispatcher.php:402
16 1.6686 7753576 JPlugin->{closure:E:\www\namespacecontent\libraries\cms\plugin\plugin.php:287-341}( ) ...\Dispatcher.php:402
17 1.6687 7753896 PlgSystemFields->onContentPrepare( ) ...\plugin.php:328
18 1.6724 7854880 FieldsHelper::extract( ) ...\fields.php:363
19 1.6736 7855552 class_exists ( ) ...\fields.php:55
20 1.6736 7855584 spl_autoload_call ( ) ...\fields.php:55
21 1.6739 7855648 JLoader::load( ) ...\fields.php:55
22 1.6749 7857704 include_once( 'E:\www\namespacecontent\administrator\components\com_content\helpers\content.php' )

The helper file is using in frontend, and would require registering auto-loader properly. That is one of the reason I didn't want to namespace the helper class while working on com_banners

avatar joomdonation
joomdonation - comment - 8 Apr 2017

Another thing is about location of layout fields and form fields class, form xmls. Since we don't have final decision about using resources folder yet, to avoid having more folders than we actually, I thnk:

  1. Form XMLs and form fields, rules should be inside Model folder

  2. View layout files should be inside View folder

Right now, you have it inside models and views folders and result in two extra folders than we need

avatar joomdonation
joomdonation - comment - 8 Apr 2017

Note that I tested this using Windows, case-sensitive folder name, views and Views for example, is different. Look like that's the reason causing layout not found error. I also installed sample data for easier testing

avatar joomla-cms-bot joomla-cms-bot - change - 10 Apr 2017
Category Administration com_content Modules Libraries Administration com_content Modules SQL Installation Libraries
avatar laoneo laoneo - change - 10 Apr 2017
The description was changed
avatar laoneo laoneo - edited - 10 Apr 2017
avatar laoneo laoneo - change - 10 Apr 2017
The description was changed
avatar laoneo laoneo - edited - 10 Apr 2017
avatar laoneo
laoneo - comment - 10 Apr 2017

@joomdonation:

  • Backend Errors:
    The errors you get are because the namespace is not filled into the table, updated the documentation what you need to do. For some reasons the namespace value gets removed after installation, despite it is in the installer script. Perhaps @mbabker has a clue as I couldn't figure out why.
  • Front end Errors:
    I will convert the front end when the back end get merged. Don't want to make this one bigger than it has to.
  • Layouts and forms XML:
    As it was decided that we don't move them to another location, I left them as before. If we are going to move them to a different location, then we do it the right way, which would be the resources folder. But I will open for that then another pr when this one gets merged.
avatar laoneo laoneo - change - 10 Apr 2017
Title
[RFC] [j4] Namespace com_content
[j4] Namespace com_content
avatar laoneo laoneo - edited - 10 Apr 2017
avatar wilsonge wilsonge - change - 11 Apr 2017
Status Pending Fixed in Code Base
Closed_Date 0000-00-00 00:00:00 2017-04-11 09:06:49
Closed_By wilsonge
avatar wilsonge wilsonge - close - 11 Apr 2017
avatar wilsonge wilsonge - merge - 11 Apr 2017

Add a Comment

Login with GitHub to post a comment