2010-07-06 1 views
1

В настоящее время я перестраиваю приложение администратора и ищу рекомендации для лучшей практики! Извините меня, если у меня нет правильной терминологии, но как мне поступить следующим образом?Какова наилучшая практика для создания моей модели?

Возьмем пример «пользователей» - как правило, мы можем создать класс со свойствами типа «имя», «имя пользователя», «пароль» и т. Д. И сделать некоторые методы, такие как getUser($user_ID), getAllUsers() и т. Д. мы получим массив/массивы пар имя-значение типа; array('name' => 'Joe Bloggs', 'username' => 'joe_90', 'password' => '123456', etc).

Проблема в том, что я хочу, чтобы этот объект знал больше о каждом из его свойств.

Рассмотрите «имя пользователя» - в дополнение к знанию его значения, я хочу, чтобы объект знал такие вещи; какая текстовая метка должна отображаться рядом с элементом управления формы, какое регулярное выражение я должен использовать при проверке, какое сообщение об ошибке подходит? Эти вещи, похоже, принадлежат модели.

Чем больше я работаю над проблемой, тем больше вижу другие вещи; какой элемент HTML следует использовать для отображения этого свойства, каковы минимальные/максимальные значения для таких свойств, как «registration_date»?

Я предусмотрел класс ищет что-то вроде этого (упрощенный):

class User { 
    ...etc... 
    private static $model = array(); 
    ...etc... 
    function __construct(){ 
     ...etc... 
     $this->model['username']['value'] = NULL; // A default value used for new objects. 
     $this->model['username']['label'] = dictionary::lookup('username'); // Displayed on the HTML form. Actual string comes from a translation database. 
     $this->model['username']['regex'] = '/^[0-9a-z_]{4,64}$/i'; // Used for both client-side validation and backend validation/sanitising; 
     $this->model['username']['HTML'] = 'text'; // Which type of HTML control should be used to interact with this property. 
     ...etc... 
     $this->model['registration_date']['value'] = 'now'; // Default value 
     $this->model['registration_date']['label'] = dictionary::lookup('registration_date'); 
     $this->model['registration_date']['minimum'] = '2007-06-05'; // These values could be set by a permissions/override object. 
     $this->model['registration_date']['maximum'] = '+1 week'; 
     $this->model['registration_date']['HTML'] = 'datepicker'; 
     ...etc... 
    } 
    ...etc... 
    function getUser($user_ID){ 
     ...etc... 
     // getUser pulls the real data from the database and overwrites the default value for that property. 
     return $this->model; 
    } 
} 

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

я мог бы использовать один и тот же объект во внутреннем интерфейсе с общим set($data = array(), $model = array()) способом, чтобы избежать отдельных методов, как setUsername($username), setRegistrationDate($registration_date) и т.д ...

  • кажется ли это как разумный подход?
  • Что бы вы назвали значением, меткой, регулярным выражением и т. Д.? Свойства свойств? Атрибуты?
  • Использование $this->model в getUser() означает, что объектная модель перезаписана, тогда как было бы лучше сохранить модель в качестве прототипа и наследовать свойства getUser().
  • Неужели я пропустил какой-то стандартный способ? (Я прошел через все рамки - пример моделей всегда не хватает !!!)
  • Как он масштабируется, когда, например, я хочу отображать типы пользователей с помощью SELECT со значениями из другой модели?

Спасибо!

Update

Я так понял, что Java имеет класс аннотаций - http://en.wikipedia.org/wiki/Java_annotations - которые, как представляется, более или менее то, что я просил. Я нашел этот пост - http://interfacelab.com/metadataattributes-in-php - Кто-нибудь знает про программирование?

+1

Вы пробовали использовать среду MVC, такую ​​как CakePHP или CodeIgniter? Я думаю, вы найдете их интересными. – quantumSoup

+0

@Aircule - Я посмотрел на множество фреймворков - Symfony, кажется, ближе всего, но я не признаю свою проблему выше в любом из своих примеров. – boatingcow

ответ

1

Рассмотрев свой вопрос, ответы и ваши ответы; Я мог бы помочь немного больше здесь (хотя сложно охватить все в одном ответе).

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

Я считаю, что вы нанесете немного анти-шаблона в своем дизайне здесь, чтобы объяснить. Вы сосредоточены на том, чтобы сделать большой кусок кода повторно используемым, валидацию, презентацию и т. Д. - но то, что вы Фактически (и, конечно, не обижайтесь) делает рабочий код приложения очень специфичным для домена, и не только это, но дизайн, который вы проиллюстрируете, сделает почти невозможным расширение, изменение слоев (например, сделать мобильную версию), (например, swap db vendors) и, тем не менее, потому что у вас есть уровни представления и приложений (и данных), смешанные вместе, любой дизайнер, попавший в приложение, должен будет работать и изменять код вашего приложения в то время, когда у вас есть две версии приложения, и у вас есть большая неприятная проблема tbh.

Как и большинство проблем программирования, вы можете решить эту проблему, делая три вещи:

  1. разработки модели предметной области.
  2. уточнение и разработка интерфейсов, а не беспокойство по поводу реализации.
  3. отделяя сквозная относится

Проектирование модели предметной области является очень важной частью программирования ОО класса на основе, если вы никогда не делали этого раньше, то сейчас самое подходящее время, это не имеет значения, является ли вам сделайте это на языке моделирования, таком как UML или просто в обычном тексте, идея состоит в том, чтобы определить все сущности в вашем домене, легко обсуждать это при написании книги при обсуждении этого вопроса, но давайте сделаем это простым. Ваша модель домена включает в себя все сущности в домене вашего приложения, каждая сущность - это вещь, думаю, пользователь, адрес, статья, продукт и т. Д., Каждый объект обычно определяется как класс (который является планом этого объекта) и каждый класс имеет свойства (например, имя пользователя, register_date и т. д.).

Class User { 
    public $username; 
    public $register_date; 
} 

Часто мы можем сохранить их как POPOs, однако они часто лучше мысль о том, как передача объектов (часто называемые объекты передачи данных, Value Objects) - простой план класса для объекта в домене - обычно мы стараемся чтобы их можно было переносить на любом языке, передавать между приложениями, сериализовывать и отправлять другим приложениям и тому подобное - это не обязательно, и в самом деле это не обязательно, но он затрагивает разделение в том смысле, что он обычно голый, подразумевая отсутствие функциональности, а только представление о сохранении значений. Резко контрастируйте с бизнес-объектами и классами полезности, которые на самом деле «делают» вещи, являются реализациями функциональности, а не просто владельцами ценностей.

Не обманывайте себя, хотя как наследование, так и состав также играют свою роль в модели домена, Пользователь может иметь несколько Адресов, каждый Адрес может быть адресом нескольких разных Пользователей. BillingAddress может распространять обычный адрес и добавлять дополнительные свойства и т. Д. (в стороне: что такое Пользователь, есть ли у вас Пользователь? есть ли у вас лицо с 1 * UserAccounts?).

После того, как вы получили свою модель домена, следующий шаг обычно отображает это до некоторой формы уровня персистентности (обычно это база данных), два общих способа сделать это (четко определенным образом) - с помощью ORM (например, доктрина, которая находится в симфонии, если я правильно помню), а другой способ - использовать шаблон DAO - я оставлю эту часть там, но обычно это отдельная часть системы, DAO layers имеют то преимущество, что вы укажите все методы, доступные для работы с уровнем персистентности для каждого объекта, сохраняя при этом абстрагирование реализации, таким образом вы можете обменять поставщиков баз данных без изменения кода приложения (или, как многие говорят, бизнес-правила).

Я собираюсь отправиться в серое место со следующим примером, как упоминалось ранее. Переносные объекты (наши сущности), как правило, являются обнаженными объектами, но они также часто являются хорошим местом для крепления других функций, Посмотрю, что я имею в виду.

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

Interface Validatable { 
    function isValid(); 
} 

то каждый из ваших объектов может осуществить это с их собственной обычной пользовательской проверки:

Class User implements Validatable { 
    public function isValid() 
    { 
    // custom validation here 
    return $boolean; 
    } 
} 

Теперь вам не нужно беспокоиться о создании некоторого запутанного способа проверки объектов, вы можете просто вызвать isValid() для любого объекта и выяснить, действительно ли это или нет.

Самое главное отметить, что, определяя интерфейс, мы отделили некоторые из проблем, поскольку никакая другая часть приложения не должна делать что-либо для проверки объекта, все, что им нужно знать, это то, что он является Validatable и вызывает метод isValid().

Однако мы перешли некоторые опасения в том, что каждый объект (экземпляр класса) теперь имеет свои собственные правила и модель проверки. Это может иметь смысл абстрагироваться это, один простой способ сделать это, чтобы сделать метод проверки статической, так что вы можете определить:

Class User { 
    public static function validate(User $user) 
    { 
    // custom validation here 
    return $boolean; 
    } 
} 

Или вы могли бы перейти к использованию методов получения и установки, это еще один очень распространенный шаблон, в котором вы можете скрыть проверку внутри сеттера, тем самым гарантируя, что каждое свойство всегда содержит достоверные данные (или значение null или значение по умолчанию).

Возможно, вы переместите валидацию в свою собственную библиотеку? Class Validate с помощью собственных методов или, может быть, вы просто поместите его в слой DAO, потому что вам нужно только что-то проверить, когда вы его сохраняете, или, может быть, вам нужно проверить, когда вы получаете данные, и когда вы это продолжаете - как вы заканчиваете это ваш звонок, и нет «лучшего способа».

Третье соображение, о котором я уже говорил, - это разделение проблем - должен ли слой настойчивости заботиться о том, как вещи, которые оно сохраняется, представлены? должна ли бизнес-логика заботиться о том, как вещи представлены? должен ли Entity заботиться о том, где и как он отображается? или должен ли слой презентации заботиться о том, как вещи представлены? Точно так же мы можем спросить, существует ли когда-нибудь один слой презентации? на одном языке? Как насчет того, как ярлык появляется в предложении, уверен, что особый пользователь и адрес имеет смысл, но вы не можете просто + s показывать списки, потому что пользователи прав, но адреса неверны;) - также у нас есть рабочие соображения, как я хочу новый дизайнер, который должен изменить код приложения, чтобы изменить представление «учетной записи пользователя» на «Учетная запись пользователя», даже если я хочу изменить код приложения в классах при запросе этого изменения?

Наконец, и просто чтобы бросить все, что я сказал, вы должны спросить себя, что я делаю здесь? Я создаю большое многоразовое приложение с потенциально многими разработчиками и долгий жизненный цикл здесь - или будет достаточно простой скрипт php для каждого представления и действия (тот, который читает $ _GET/$ _ POST, проверяет, сохраняет в db, затем отображает то, что он должен или перенаправляет туда, где он должен) - во многих, если не во всех случаях, это все, что нужно.

Помните, что PHP вызывается, когда запрос выполняется на веб-сервер, а затем отправляет ответ [end], то есть то, что происходит между вами, это ваш домен, ваша работа, клиент и пользователь обычно не делают Не волнуйтесь, и вы можете подытожить то, что вы пытаетесь сделать это просто: постройте скрипт, чтобы как можно быстрее ответить на этот запрос с ожидаемыми результатами. Вот и это не должно быть сложнее.

Чтобы быть грубым, делая все, о чем я упоминал, и многое другое - это замечательная вещь, вы научитесь загружать, лучше понимаете свою работу и т. Д., Но если вы просто хотите получить работу за дверь и иметь простой в обслуживании простой код в конце, просто создайте один сценарий для каждого представления и один за действие, с нечетным многоразовым битом (например, обработчик http, класс db, класс электронной почты и т. д.).

+0

Я очень ценю время, которое вы приняли, чтобы написать свои ответы. Вышеизложенное сделало бы замечательное введение в статью, например «зачем использовать фреймворк». Однако, просто подумав об этом регулярном выражении пользователя, поскольку я хочу использовать метод isValid(), подпрограмму jQuery validate(), всплывающую подсказку sprintf/l10n HTML, документацию phpdoc и, возможно, генератор схемы db, чтобы процитировать его, это имело смысл что он должен жить в одном удобном месте. Домен 'myApp' >> class 'User' >> property 'username' >> attribute 'regex' - это неправильное место? Регулярное выражение используется не только для проверки ... – boatingcow

+0

... чтобы добавить к путанице, некоторые атрибуты могут отличаться для разных пользователей и могут возникать из таблицы разрешений при создании экземпляра модели; у суперпользователя нет ограничений на редактирование ['registration_date'], как я писал в приведенном выше примере, но пользователь приема может быть ограничен конкретными датами с ['registration_date'] ['minimum'] и ['registration_date'] [ 'максимум']. – boatingcow

+0

@boatingcow снова вы перепутали разные вещи, domainmodel и проверка свойств - это «действительное имя пользователя», «это действительная дата» - то, о чем вы говорите, - это бизнес-правила », если вы вошли в систему пользователя имеет роль приемного пользователя, а затем проверяет дату регистрации в пределах (x, y) «две совершенно разные вещи, которые имеют разные места в приложении - честно говоря, забудьте о структуре, с которой вы столкнулись, напишите все это в коде в классах и методы с if's then refactor from there;) – nathan

0

Вы работаете в архитектуре Model-View-Controller (MVC).

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

C управляет логикой манипулирования этой информацией. Он изменяет M в ответ на ввод пользователя.

V - это часть, которая обрабатывает вещи. Это должно быть что-то вроде шаблонов Smarty, а не огромное количество необработанного PHP для генерации HTML.

Наличие всего «в одном месте» - неправильный подход. У вас не будет дублированного кода с MVC - каждая часть представляет собой отдельный шаг. Это улучшает повторное использование кода, удобочитаемость и ремонтопригодность.

+0

Некоторые критические замечания: 1) Модель может быть более сложной, чем пары с ключом. 2) Представления не должны создаваться с помощью шаблонной структуры, как таковой. Нет ничего плохого в использовании «raw» PHP для генерации HTML. Ограничение состоит в том, что единственная логика, которая существует в представлениях, должна быть связана с отображением данных. Взгляды вообще должны быть относительно «простыми». 3) Контроллер будет делать больше, чем просто внести изменения в модель. Он также, вероятно, определит, какие представления подходят, и, возможно, потребуется вызвать другие контроллеры. –

+0

Я попытался сохранить свой проблемный шаблон-агностик. Я склонен обнаруживать, что MVC на самом деле немного упрощает, когда дело доходит до веб-приложений. MVVP - это самый близкий образец того, на что я нацелен в настоящий момент, но опять же, разве это не парадигма рабочего стола и не обязательно означает для Интернета? – boatingcow

3

Вы на правильном пути. Когда дело доходит до моделей, я думаю, что существует много подходов, а «правильный» обычно зависит от вашего типа приложения.

Ваша модель может быть непосредственно Active Record, может быть table row data gateway или «POPO», простой старый объект PHP (другими словами, класс, который не реализует какой-либо конкретный шаблон).

Независимо от того, что вы решили работать лучше всего для вас, такие вещи, как проверка и т. Д., Могут быть помещены в класс модели. Вы должны работать с пользователями как объекты пользователя, а не как ассоциативные массивы - это главное.

ли это, кажется, как разумный подход

Да, к тому же форма этикетки вещь. Вероятно, лучше всего иметь отдельный источник данных, таких как метки форм, потому что в конечном итоге вы захотите их локализовать. Кроме того, метка на самом деле не связана с объектом пользователя - она ​​связана с отображением формы.

Как бы подойти к этому (предложение)

Я бы иметь объект пользователя, который представляет одного пользователя. Должно быть возможно создать пустого пользователя или создать его из массива (так что его легко создать из результата базы данных, например). Пользовательский объект также должен иметь возможность проверять себя, например, вы могли бы дать ему метод «isValid», который при вызове проверяет все значения на достоверность.

У меня также был бы класс репозитория пользователя (или, возможно, только некоторые статические методы в классе User), который можно было бы использовать для извлечения пользователей из базы данных и хранения их обратно. Этот репозиторий будет напрямую возвращать пользовательские объекты при извлечении и принимать объекты пользователя в качестве параметров для сохранения.

Что касается форм, возможно, у вас может быть класс формы, который принимает объект пользователя. Затем он может автоматически получать значения от пользователя и использовать его для проверки себя.

Я написал на эту тему немного здесь: http://codeutopia.net/blog/2009/02/28/creating-a-simple-abstract-model-to-reduce-boilerplate-code/, а также некоторые другие сообщения, связанные в конце этого.

Надеюсь, это поможет. Я просто хотел бы напомнить, что мой подход не идеален. =)

+0

Я полностью согласен с вашим подходом, и это почти то, что у меня уже есть. Я не был слишком уверен в том, где должны жить атрибуты свойств или где должны появляться значения по умолчанию (модель или запись в базе данных?). Метки формы в настоящее время живут в объекте, который поступает из базы данных поиска, к которой осуществляется доступ через статический метод lookup() - вы видели недостаток? – boatingcow

+0

... еще один вопрос: если я использую метод типа isValid(), то этот метод должен знать, например, что такое регулярное выражение для имени пользователя. Такое же регулярное выражение используется в всплывающей подсказке в форме: валидатор jQuery, мой валидатор POST, описание схемы db и любая сопроводительная документация, которую я выпускаю. Где лучшее место для хранения регулярных выражений? – boatingcow

+0

Если вам нужно поделиться частями вашей проверки больше, вы можете создать отдельные классы валидатора для выполнения таких обязанностей, как проверка имени пользователя. Значения по умолчанию для свойств могут быть непосредственно в самом объекте по умолчанию, иначе значения в них могут быть загружены из любого места, где вы хотите. В типичном случае вы загружаете значения из формы при создании новых объектов или из базы данных при загрузке существующих. –

2

Абстрактный отклик для вас, который, вполне возможно, совсем не поможет, но я рад получить минус голоса, поскольку это стоит сказать:)

Здесь вы имеете дело с двумя разными моделями, в каком-то мире мы называем этот класс и экземпляр, в других мы говорим о классах и физических лицах, а в других мирах мы делаем различия между операторами A-Box и T-Box ,

Вы имеете дело с двумя наборами данных здесь, я буду писать их в виде простого текста:

User a Class . 
username a Property; 
    domain User; 
    range String . 
registration_date a Property; 
    domain User; 
    range Date . 

это ваши данные класса, отчеты T-Box, светокопии, как вы описали вселенную, которая ваше приложение - это не описание «вещей» в вашей вселенной, а использовать это, чтобы описать вещи в вашей вселенной, ваши данные экземпляра .. так что вы затем:

user1 a User ; 
    username "bob"; 
    registration_date "2010-07-02" . 

который является вашим Instance, Individual, A-Box, вещи во вселенной.

Здесь вы можете заметить, что все другие вещи, которые вы зададите, проверяете, добавляете метки к свойствам и т. Д., Все подпадают под первую группу, вещи, которые описывают вашу вселенную, а не вещи в ней. Так вот, где вы хотите добавить его .. снова в обычном тексте ..

username a Property; 
    domain User; 
    range String; 
    title "Username"; 
    validation [ type Regex; value '/^[0-9a-z_]{4,64}$/i' ] . 

Точка во всем этом, чтобы помочь вам проанализировать другие ответы, которые Вы получаете - вы заметите, что в вашем предложении вы объединили эти два разных набора данных и, в некотором смысле, это хорошо. Из этого, надеюсь, вы увидите, что обычно классы в PHP берут на себя роль классов (неудивительно) и каждого объекта (или экземпляра класса) содержит отдельные данные экземпляра, однако вы начали объединять эти две части вашего юниверса вместе, чтобы попытаться сделать один большой многоразовый набор классов вне предложенных языковых конструкций PHP.

Оттуда у вас есть два пути, вы можете либо свернуть в линию и следовать языковой структуре, чтобы сделать код полуразмножимым и следовать предложенным шаблонам, как MVC (который, если вы еще этого не сделали, сделал бы вас хорошо) - или вы можете отправиться в передний край мира, где описываются эти миры, и мы строим рамки для понимания данных о наших юниверсах и вещах в нем, но это абстрактное место, где в тот момент трудно быть продуктивным, хотя в долгосрочный путь к будущему.

Независимо от этого, я надеюсь, что так или иначе это поможет вам получить контроль над другими ответами.

Всего наилучшего!

+0

Хороший материал, но я волнуюсь, что это немного над чьей-то головой, которая только начинается :) Esp. так как ваши примеры в Erlang или что-то –

+0

@Jani полностью согласен с Jani, иногда вы пишете ответы так же, как и сами, задавая вопрос, - примеры просто упрощены. RDF/EAV btw - мне интересно узнать, над головой или нет, в одном отношении я бы сказал, что упоминание Активных Записей и ПОПО над головой, но на самом деле я бы предложил, чтобы это было над головой - мы увидим - рассмотрим этот ответ как дополнение других , включая ваши: D – nathan

+0

Это, вероятно, будет служить той же цели, о которой я имел в виду, когда упоминал эти шаблоны - открыть некоторые новые двери для обучения больше =) –