Использование сеттера с многомерными массивами - PullRequest
1 голос
/ 04 мая 2011

Я пытаюсь создать __set для объекта в PHP, который работает с многомерными массивами.Это вообще возможно?

Я бы хотел иметь что-то вроде следующего: $post->comments[0]['uid']=3;.Тем не менее, комментарии на самом деле будут ключом в частной переменной кэша $_cache['comments']=array().Было бы хорошо, если бы функция __set могла как-то получить и базовый ключ (комментарии), и индекс (0), а также ключ / значение, которые она устанавливает (uid / 3).Однако это невозможно.

Я думал о создании $_cache['comments'] и массива ArrayObjects, но это не позволило бы мне определить пользовательскую перегрузку _ get / _set.Вместо этого я думаю, что мне может понадобиться создать новый объект Comments, а затем заполнить их этим массивом.Однако я бы не хотел этого делать, и было бы здорово, если бы PHP мог обрабатывать вложенные массивы при перегрузках __set.

Я использую Mongo и хотел бы, чтобы у меня был только один объектза каждый документ.Тем не менее, объекты массивов в Mongo создают для меня небольшую проблему.Я хотел бы просто обрабатывать их как массив в PHP, но это не представляется возможным.Сеттер должен взять $post->comments[0]['uid']=3 и обновить как кэш, так и значение $this->data['comments'][0]['uid']=3.

Я знаю, что если бы комментарии были массивом объектов, я мог бы сделать это:

$post->comments[0]->uid=3; 
///Sets $_cache['comments'][0]->uid=3;

И это сработало бы, потому что получатель комментариев возвратил бы массив объектов и разрешил бы ему доступсвойство uid.Затем я мог бы получить / установить в объекте комментариев, который каким-то образом отредактировал бы $post->data через псевдо-«функцию» друга / хак.Тем не менее, я не вижу простой способ сделать это с массивами ....

Любой совет?

Ответы [ 5 ]

1 голос
/ 16 декабря 2014

Практически ничего не стоит написать в чатах PHP или документации php, которая будет вам полезна, Адам.Большинство предложений имеют тенденцию к реализации interface ArrayAccess или расширению class ArrayObject, оба в SPL.Фактически, существует удивительно простое решение вашей проблемы: $post->comments[0]['uid']=3 с использованием перегруженного сеттера __set().

Определение private $comments = array(); в классе post.Для удобства используйте текстовый ключ для первого нижнего индекса $comments: здесь целое число 0 становится, скажем, «нулем».Затем вы вызываете установщик следующим образом:

$post->zero = ['uid', 3];

Это вызывает магический установщик, потому что в классе post публично не объявлено свойство $zero: «Методы перегрузки вызываются при взаимодействии со свойствами или методамикоторые не были объявлены или не видны в текущей области. "(Страница справочника PHP 5 на Перегрузка .)

Установщик также может быть setComments(), что удобно, потому что вам не придется различать входящие свойства, чтобы идентифицировать те, которые предназначены для массива comments, но синтаксис вызова становится менее естественным.

Ваша перегруженная автомагическая функция __set получает два аргумента: свойство и значение:

public function __set($property, $value) {

, очень напоминающее КрокфордаПротокол JSON.Полезно думать об этом в этих терминах.

Поскольку отправленное вами свойство "ноль" не существует в классе post, его необходимо перехватить, и мой предпочтительный метод, начиная с первого индексав свойстве comments, скорее всего, будет иметь несколько значений, для определения частного массива поддерживаемых значений нижнего индекса в post:

private $indices = [
     "zero"  => 0,
     "one"   => 1,
     "two"   => 2,
     "three" => 3 
];

Когда индекс для comments поступает в __set() как $property подтверждено существование в $indices.Теперь вы просто перебираете массив, предоставленный в $value, извлекаете uid и соответствующее ему значение, а затем присваиваете $comments следующим образом:

public function __set($property, $value) {
    if (array_key_exists($property, $this->indices) && is_array($value))
        foreach ($value as $uid => $uid_value)  
            $this->comments[$this->indices[property]][$uid] = $uid_value;
    else
        ...
}

с использованием $this->indices[property] для извлечения целого числазначение 0, которое будет использоваться для индексации первого измерения comments, и $uid_value, извлеченное со значением присваиваемого значения int 3.

Подход, описанный здесь, не является уловкой, обходным решением или умным приемом.Это простая методика проектирования, предназначенная для работы с одним из средств SPL, и, в принципе, ее можно распространить на массивы произвольного измерения.У меня есть проект, реализованный в производственной системе, поэтому, если у вас все еще есть проблемы, напишите здесь, и я помогу вам отладить ваше приложение.Желаем удачи!

1 голос
/ 05 мая 2011

Я перепрыгнул через некоторые из этих обручей, когда создавал свой собственный класс-оболочку PHP.

https://github.com/gatesvp/MongoModel

Он все еще в работе, но он обрабатывает некоторые базовые "карты этого объектав БД ".

1 голос
/ 04 мая 2011

Это сложнее, чем вы себе представляете. Вы можете выполнить то, что вы хотите, с кучей обходных путей, но это редко стоит усилий.

Если ->comments само разрешено методом getter, то присвоение чего-либо для подмассива [0] на самом деле не окажется в частной собственности. И ->comments[0]= даже не вызовет ваш метод установки. Вместо этого это доступ на чтение.

Чтобы вообще сработать, вы должны заставить свой метод __get возвращать ссылку & $this->_cache['comments'].

Если вы хотите перехватить доступ к множеству в этом массиве comments, вам действительно понадобится ArrayObject. Разница в том, что для этого необходимо переопределить offsetGet и offsetSet вместо __get и __set. Но опять же, поскольку вы обращаетесь к другому подмассиву, метод __get будет фактически использован, и вам нужно будет вернуть другую ссылку или еще раз уровень обходного пути ArrayObject.

0 голосов
/ 04 мая 2011

Создайте функцию вместо того, чтобы пытаться взломать ее поверх чего-то, что даже не предназначено для этого.

public function setCommentUid($commentId, $uid) {
    $this->_cache['comments'][$commentId]->uid = $uid;
}

//then...
$post->setCommentUid(0, 3);

Это значительно упрощает использование класса, и намного легче увидеть, чтоэто делает.

0 голосов
/ 04 мая 2011

Я полагаю, что для перегрузки некоторых свойств вы можете использовать магический метод __set (), определенный здесь: http://us.php.net/__set

Я не уверен, что вы можете обработать [0] до того, как оно будет принятоPHP-компилятором ...

Таким образом, ваше другое решение - преобразовать комментарии в метод

public function comments($id) {
   return $this->obj[$id]; // Obj
}

И возвращаемый вами объект имеет свойство __set

class Obj {
   private $id;
   public function __set($key, $value) {
       if($key === 'uid') {
           $_cache = $GLOBALS['_cache'];
           $_cache['comments'][$this->id]->uid = $value;
       }
   }
}

Здесь много кода отсутствует, но вы можете выяснить, как это сделать с помощью этого метода __set ()

...