3 namespace Drupal\Core\Menu;
5 use Drupal\Core\Config\ConfigFactoryInterface;
8 * Defines an implementation of the menu link override using a config file.
10 class StaticMenuLinkOverrides implements StaticMenuLinkOverridesInterface {
13 * The config name used to store the overrides.
15 * This configuration can not be overridden by configuration overrides because
16 * menu links and these overrides are cached in a way that is not override
21 protected $configName = 'core.menu.static_menu_link_overrides';
24 * The menu link overrides config object.
26 * @var \Drupal\Core\Config\Config
31 * The config factory object.
33 * @var \Drupal\Core\Config\ConfigFactoryInterface
35 protected $configFactory;
38 * Constructs a StaticMenuLinkOverrides object.
40 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
41 * A configuration factory instance.
43 public function __construct(ConfigFactoryInterface $config_factory) {
44 $this->configFactory = $config_factory;
48 * Gets the configuration object when needed.
50 * Since this service is injected into all static menu link objects, but
51 * only used when updating one, avoid actually loading the config when it's
54 protected function getConfig() {
55 if (empty($this->config)) {
56 // Get an override free and editable configuration object.
57 $this->config = $this->configFactory->getEditable($this->configName);
65 public function reload() {
67 $this->configFactory->reset($this->configName);
73 public function loadOverride($id) {
74 $all_overrides = $this->getConfig()->get('definitions');
75 $id = static::encodeId($id);
76 return $id && isset($all_overrides[$id]) ? $all_overrides[$id] : [];
82 public function deleteMultipleOverrides(array $ids) {
83 $all_overrides = $this->getConfig()->get('definitions');
85 foreach ($ids as $id) {
86 $id = static::encodeId($id);
87 if (isset($all_overrides[$id])) {
88 unset($all_overrides[$id]);
93 $this->getConfig()->set('definitions', $all_overrides)->save();
101 public function deleteOverride($id) {
102 return $this->deleteMultipleOverrides([$id]);
108 public function loadMultipleOverrides(array $ids) {
111 $all_overrides = $this->getConfig()->get('definitions') ?: [];
112 foreach ($ids as $id) {
113 $encoded_id = static::encodeId($id);
114 if (isset($all_overrides[$encoded_id])) {
115 $result[$id] = $all_overrides[$encoded_id];
125 public function saveOverride($id, array $definition) {
126 // Only allow to override a specific subset of the keys.
134 // Filter the overrides to only those that are expected.
135 $definition = array_intersect_key($definition, $expected);
136 // Ensure all values are set.
137 $definition = $definition + $expected;
139 // Cast keys to avoid config schema during save.
140 $definition['menu_name'] = (string) $definition['menu_name'];
141 $definition['parent'] = (string) $definition['parent'];
142 $definition['weight'] = (int) $definition['weight'];
143 $definition['expanded'] = (bool) $definition['expanded'];
144 $definition['enabled'] = (bool) $definition['enabled'];
146 $id = static::encodeId($id);
147 $all_overrides = $this->getConfig()->get('definitions');
148 // Combine with any existing data.
149 $all_overrides[$id] = $definition + $this->loadOverride($id);
150 $this->getConfig()->set('definitions', $all_overrides)->save(TRUE);
152 return array_keys($definition);
158 public function getCacheTags() {
159 return $this->getConfig()->getCacheTags();
163 * Encodes the ID by replacing dots with double underscores.
165 * This is done because config schema uses dots for its internal type
166 * hierarchy. Double underscores are converted to triple underscores to
167 * avoid accidental conflicts.
170 * The menu plugin ID.
173 * The menu plugin ID with double underscore instead of dots.
175 protected static function encodeId($id) {
176 return strtr($id, ['.' => '__', '__' => '___']);