Completed
Push — master ( 71e3f4...1ed264 )
by Marin
02:34
created

Nav_Menu_Container::set_datastore()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Carbon_Fields\Container;
4
5
use Carbon_Fields\Datastore\Meta_Datastore;
6
use Carbon_Fields\Datastore\Nav_Menu_Datastore;
7
use Carbon_Fields\Walker\Nav_Menu_Edit_Walker;
8
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
9
10
/**
11
 * Nav menu item fields container class. 
12
 */
13
class Nav_Menu_Container extends Container {
14
15
	public static $instances = array();
16
	public static $active_containers = array();
17
	public static $initialized = false;
18
19
	/**
20
	 * Create a new nav menu item fields container
21
	 *
22
	 * @param string $id ID of the container
23
	 **/
24
	public function __construct( $id ) {
25
		// Reset the registered fields array, this is required so we can have fields with same names
26
		self::$registered_field_names = array();
27
28
		$id = str_replace( ' ', '', ucwords( str_replace( '_', ' ', $id ) ) );
29
30
		$this->id = $id;
31
32
		$this->store = new Nav_Menu_Datastore();
33
34
		self::initialize_filters();
35
	}
36
37
	/**
38
	 * Perform instance initialization after calling setup()
39
	 * 
40
	 * @param int $menu_id Used to pass the correct menu_item_id to the Container object
41
	 * @param bool $render Whether the container will render the fields.
42
	 */
43
	public function init( $menu_id = 0, $render = true ) {
44
		$this->set_menu_id( $menu_id );
45
46
		$this->load();
47
		$this->_attach();
48
49
		if ( ! empty( $menu_id ) && $render === true ) {
50
			$this->render();
51
		}
52
53
		return $this;
54
	}
55
56
	/**
57
	 * Assign DataStore instance for use by the container fields
58
	 *
59
	 * @param object $store
60
	 **/
61
	public function set_datastore( Meta_Datastore $store ) {
62
		parent::set_datastore( $store );
63
	}
64
65
	/**
66
	 * Set the menu item ID the container will operate with.
67
	 *
68
	 * @param int $menu_id
69
	 **/
70
	public function set_menu_id( $menu_id ) {
71
		$this->menu_id = $menu_id;
72
		$this->store->set_id( $menu_id );
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Carbon_Fields\Datastore\Datastore_Interface as the method set_id() does only exist in the following implementations of said interface: Carbon_Fields\Datastore\Comment_Meta_Datastore, Carbon_Fields\Datastore\Meta_Datastore, Carbon_Fields\Datastore\Nav_Menu_Datastore, Carbon_Fields\Datastore\Post_Meta_Datastore, Carbon_Fields\Datastore\Term_Meta_Datastore, Carbon_Fields\Datastore\User_Meta_Datastore.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
73
	}
74
75
	/**
76
	 * Checks whether the current request is valid
77
	 * 
78
	 * @return bool
79
	 **/
80
	public function is_valid_save() {
81
		return true;
82
	}
83
84
	/**
85
	 * Perform save operation after successful is_valid_save() check.
86
	 * The call is propagated to all fields in the container.
87
	 **/
88
	public function save( $user_data = null ) {
89
		foreach ( $this->fields as $field ) {
90
			$field->set_value_from_input();
91
			$field->save();
92
		}
93
94
		do_action( 'carbon_after_save_nav_menu', $this );
95
	}
96
97
	/**
98
	 * Returns an array that holds the container data, suitable for JSON representation.
99
	 * This data will be available in the Underscore template and the Backbone Model.
100
	 * 
101
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
102
	 * @return array
103
	 */
104
	public function to_json( $load ) {
105
		$carbon_data = parent::to_json( false );
106
107
		// Sends the menu_id to javascript
108
		$carbon_data = array_merge( $carbon_data, array(
109
			'menu_id' => $this->menu_id,
110
		) );
111
112
		return $carbon_data;
113
	}
114
115
	/**
116
	 * Output the container markup
117
	 **/
118
	public function render() {
119
		include \Carbon_Fields\DIR . '/templates/Container/nav_menu.php';
120
	}
121
122
	/**
123
	 * TODO: make sure the containers for nav menus are not printed everywhere
124
	 */
125
	public function is_valid_attach() {
126
		return true;
127
	}
128
129
	/**
130
	 * Initialize filters. This will be executed only once
131
	 */
132
	public static function initialize_filters() {
133
		if ( self::$initialized ) {
134
			return;
135
		}
136
137
		$self = 'Carbon_Fields\Container\Nav_Menu_Container';
138
		add_action( 'crb_print_carbon_container_nav_menu_fields_html', array( $self, 'form' ), 10, 5 );
139
		add_filter( 'wp_edit_nav_menu_walker', array( $self, 'edit_walker' ), 10, 2 );
140
		add_action( 'wp_update_nav_menu_item', array( $self, 'update' ), 10, 3 );
141
	}
142
143
	/**
144
	 * Get containers only once, and store in instance memory.
145
	 */
146
	public static function get_containers() {
147
		if ( empty( self::$active_containers ) ) {
148
			self::$active_containers = Container::get_active_containers();
149
		}
150
151
		return self::$active_containers;
152
	}
153
154
	/**
155
	 * Render custom fields inside each Nav Menu entry
156
	 */
157
	public static function form( $item ) {
158
		self::set_instance_for_id( $item->ID, true );
159
	}
160
161
	/**
162
	 * Setup custom walker for the Nav Menu entries
163
	 */
164
	public static function edit_walker() {
165
		return 'Carbon_Fields\Walker\Nav_Menu_Edit_Walker';
166
	}
167
168
	/**
169
	 * Trigger Save for all instances
170
	 */
171
	public static function update( $menu_id, $current_menu_item_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $menu_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
172
		$instance = self::set_instance_for_id( $current_menu_item_id, false );
173
		$instance->_save();
174
175
		return $instance;
176
	}
177
178
	/**
179
	 * Render attribute prevents field containers showing on menu save
180
	 */
181
	public static function set_instance_for_id( $current_menu_item_id, $render = true ) {
182
		$active_containers = self::get_containers();
183
		$suffix = '-' . $current_menu_item_id;
184
185
		foreach ( $active_containers as $container ) {
186
			if ( $container->type != 'Nav_Menu' ) {
187
				continue;
188
			}
189
190
			$custom_fields = array();
191
			$fields = $container->get_fields();
192
193
			foreach ( $fields as $field ) {
194
				$tmp_field = clone $field;
195
196
				// Setup Public properties
197
				$tmp_field->current_menu_item_id = $current_menu_item_id;
198
				$tmp_field->initial_name = $tmp_field->get_name();
199
200
				// Setup Field ID and Name
201
				$tmp_field->set_id( $tmp_field->get_id() . $suffix );
202
				$tmp_field->set_name( $tmp_field->get_name() . $suffix );
203
204
				// Update Datastore instance
205
				$new_datastore = new Nav_Menu_Datastore();
206
				$new_datastore->set_id( $current_menu_item_id );
207
				$tmp_field->set_datastore( $new_datastore );
208
209
				$custom_fields[] = $tmp_field;
210
			}
211
212
			self::$instances[ $current_menu_item_id ] = Container::factory( 'nav_menu', $container->id . $suffix )
213
				->add_fields( $custom_fields )
214
				->init( $current_menu_item_id, $render );
215
		}
216
217
		return self::$instances[ $current_menu_item_id ];
218
	}
219
}