From 1144e49b8e00fa75b1593e4637c9218d7d944b97 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 12 Jan 2014 21:33:46 +0100 Subject: Big quickstart doc overhaul, pt. 1 --- .../protected/pages/Fundamentals/Applications.page | 4 + .../protected/pages/Fundamentals/Architecture.page | 25 +- .../protected/pages/Fundamentals/Components.page | 360 --------------------- .../protected/pages/Fundamentals/Components1.page | 157 +++++++++ .../protected/pages/Fundamentals/Components2.page | 207 ++++++++++++ .../protected/pages/Fundamentals/Pages.page | 14 +- .../protected/pages/Fundamentals/Services.page | 5 +- 7 files changed, 407 insertions(+), 365 deletions(-) delete mode 100755 demos/quickstart/protected/pages/Fundamentals/Components.page create mode 100755 demos/quickstart/protected/pages/Fundamentals/Components1.page create mode 100644 demos/quickstart/protected/pages/Fundamentals/Components2.page (limited to 'demos/quickstart/protected/pages/Fundamentals') diff --git a/demos/quickstart/protected/pages/Fundamentals/Applications.page b/demos/quickstart/protected/pages/Fundamentals/Applications.page index 8e0b9e9a..71901170 100755 --- a/demos/quickstart/protected/pages/Fundamentals/Applications.page +++ b/demos/quickstart/protected/pages/Fundamentals/Applications.page @@ -7,7 +7,11 @@ An application is an instance of TApplication or its derived class. It

Applications are configured via application configurations. They are usually created in entry scripts like the following, +// load the prado entry script require_once('/path/to/prado.php'); +// or, if using composer: +require_once('vendor/autoload.php'); + $application = new TApplication; $application->run(); diff --git a/demos/quickstart/protected/pages/Fundamentals/Architecture.page b/demos/quickstart/protected/pages/Fundamentals/Architecture.page index 10f16c3f..c6bee7de 100755 --- a/demos/quickstart/protected/pages/Fundamentals/Architecture.page +++ b/demos/quickstart/protected/pages/Fundamentals/Architecture.page @@ -2,12 +2,31 @@

Architecture

-PRADO is primarily a presentational framework, although it is not limited to be so. The framework focuses on making Web programming, which deals most of the time with user interactions, to be component-based and event-driven so that developers can be more productive. The following class tree depicts some of the major classes provided by PRADO, +PRADO is primarily a presentational framework, although it is not limited to be so. The framework focuses on making Web programming, which deals most of the time with user interactions, to be component-based and event-driven so that developers can be more productive. The following class tree depicts some of the major classes provided by PRADO:

-When a PRADO application is processing a page request, its static object diagram can be shown as follows, +When a PRADO application is processing a page request, its static object diagram can be shown as follows:

- + +

+Once the main Application object gets created, it load the application configuration. +The minimal configuration defines a set of basic modules to parse the Request, create a proper Response, mantain the user Session, handle any Error and publish the needed Assets (images, css, javascript, etc). These helpers module will be available from anywhere inside the application code. +
+Additionally, any 3rd-party or custom module can be loaded, and external Parameters can be loaded from external configurations. +

+ +

+Once the basic infrastructure has been set up, the Request module parses the request trying to identify the requested route. Different routes can be handled by different services, but the default route for http requests is the Page Service. +
+The Page Service's role is to instanciate the requested Page, run it, apply any defined Theme and grab the result in order to build the Response. +

+ +

+A Page can be a simple script (.php), a Template (.page), or both. PRADO uses a very powerful template engine where Controls can be instanciated directly. +
+A Control is an self-contained widget that fullfills a specific task; they can be a simple script (.php), a Template (.page), or both. +

+ \ No newline at end of file diff --git a/demos/quickstart/protected/pages/Fundamentals/Components.page b/demos/quickstart/protected/pages/Fundamentals/Components.page deleted file mode 100755 index 11235b56..00000000 --- a/demos/quickstart/protected/pages/Fundamentals/Components.page +++ /dev/null @@ -1,360 +0,0 @@ - -

Components

-

-A component is an instance of TComponent or its child class. The base class TComponent implements the mechanism of component properties and events. -

- -

Component Properties

-

-A component property can be viewed as a public variable describing a specific aspect of the component, such as the background color, the font size, etc. A property is defined by the existence of a getter and/or a setter method in the component class. For example, in TControl, we define its ID property using the following getter and setter methods, - -class TControl extends TComponent { - public function getID() { - ... - } - public function setID($value) { - ... - } -} - -

-

-To get or set the ID property, do as follows, just like working with a variable, - -$id = $component->ID; -$component->ID = $id; - -This is equivalent to the following, - -$id = $component->getID(); -$component->setID( $id ); - -

-

-A property is read-only if it has a getter method but no setter method. Since PHP method names are case-insensitive, property names are also case-insensitive. A component class inherits all its ancestor classes' properties. -

- -

Subproperties

-

-A subproperty is a property of some object-typed property. For example, TWebControl has a Font property which is of TFont type. Then the Name property of Font is referred to as a subproperty (with respect to TWebControl). -

-

-To get or set the Name subproperty, use the following method, - -$name = $component->getSubProperty('Font.Name'); -$component->setSubProperty('Font.Name', $name); - -This is equivalent to the following, - -$name = $component->getFont()->getName(); -$component->getFont()->setName( $name ); - -

- -

Js-friendly properties

-

-A JavaScript-friendly property is a property that can accept both simple strings and raw javascript. -Prado automatically encodes all properties sent clientside inside javascript blocks to avoid security problems (like injections or cross site scripting). -If a property is known to always contain only safe javascript code and its value needs to bypass this encoding, that property can be defined in a special way that will make Prado mark its value as "safe". -Js-friendly properties are identified by their name starting with 'js' (case insensitive): - -// getter, defines a readable property 'Text' -function getJsText() { … } -// setter, defines a writable property 'Text', with $value being the value to be set to the property -function setJsText(TJavaScriptLiteral $value) { … } - -Js-friendly properties can be accessed using both their Js-less name and their Js-enabled name: - -// set some simple text as property value -$component->Text = 'text'; -// set some javascript code as property value -$component->JsText = 'raw javascript'; - -In the first case, the property value will automatically gets encoded when sent clientside inside a javascript block. -In the second case, the property will be 'marked' as being a safe javascript statement and will not be encoded when rendered inside a javascript block. -This special handling makes use of the TJavaScriptLiteral class. -

- - -

Component Events

-

-Component events are special properties that take method names as their values. Attaching (setting) a method to an event will hook up the method to the places at which the event is raised. Therefore, the behavior of a component can be modified in a way that may not be foreseen during the development of the component. -

-

-A component event is defined by the existence of a method whose name starts with the word on. The event name is the method name and is thus case-insensitve. For example, in TButton, we have - -class TButton extends TWebControl { - public function onClick( $param ) { - ... - } -} - -This defines an event named OnClick, and a handler can be attached to the event using one of the following ways, - -$button->OnClick = $callback; -$button->OnClick->add( $callback ); -$button->OnClick[] = $callback; -$button->attachEventHandler( 'OnClick' , $callback ); - -

- The variable $callback contains the definition of the event handler that can be either a string referring to a global function name, or an array whose first element refers to an object and second element a method name/path that is reachable by the object, e.g. -

- - -

Global events

-

-With the addition of behaviors, a more expansive event model is needed. There -are two new event types (global and dynamic events) as well as a more comprehensive -behavior model that includes class wide behaviors. -

-

-A global event is defined by all events whose name starts with 'fx'. -The event name is potentially a method name and is thus case-insensitive. All 'fx' events -are valid as the whole 'fx' event/method space is global in nature. Any object may patch into -any global event by defining that event as a method. Global events have priorities -just like 'on' events; so as to be able to order the event execution. Due to the -nature of all events which start with 'fx' being valid, in effect, every object -has every 'fx' global event. It is simply an issue of tapping into the desired -global event. -

-

-A global event that starts with 'fx' can be called even if the object does not -implement the method of the global event. A call to a non-existing 'fx' method -will, at minimal, function and return null. If a method argument list has a first -parameter, it will be returned instead of null. This allows filtering and chaining. -'fx' methods do not automatically install and uninstall. To install and uninstall an -object's global event listeners, call the object's listen and -unlisten methods, respectively. An object may auto-install its global event -during __construct by overriding getAutoGlobalListen and returning true. -

-

-As of PHP version 5.3, nulled objects without code references will still continue to persist -in the global event queue because __destruct is not automatically called. In the common -__destruct method, if an object is listening to global events, then unlisten is called. -unlisten is required to be manually called before an object is -left without references if it is currently listening to any global events. This includes -class wide behaviors. -

-

-An object that contains a method that starts with 'fx' will have those functions -automatically receive those events of the same name after listen is called on the object. -

-

-An object may listen to a global event without defining an 'fx' method of the same name by -adding an object method to the global event list. For example -

- -$component->fxGlobalCheck=$callback; // or $component->OnClick->add($callback); -$component->attachEventHandler('fxGlobalCheck',array($object, 'someMethod')); - -

Events between Objects and their behaviors, Dynamic Events

-

-An intra-object/behavior event is defined by methods that start with 'dy'. Just as with -'fx' global events, every object has every dynamic event. Any call to a method that -starts with 'dy' will be handled, regardless of whether it is implemented. These -events are for communicating with attached behaviors. -

-

-Dynamic events can be used in a variety of ways. They can be used to tell behaviors -when a non-behavior method is called. Dynamic events could be used as data filters. -They could also be used to specify when a piece of code is to be run, eg. should the -loop process be performed on a particular piece of data. In this way, some control -is handed to the behaviors over the process and/or data. -

-

-If there are no handlers for an 'fx' or 'dy' event, it will return the first -parameter of the argument list. If there are no arguments, these events -will return null. If there are handlers an 'fx' method will be called directly -within the object. Global 'fx' events are triggered by calling raiseEvent. -For dynamic events where there are behaviors that respond to the dynamic events, a -TCallChain is developed. A call chain allows the behavior dynamic event -implementations to call further implementing behaviors within a chain. -

-

-If an object implements IDynamicMethods, all global and object dynamic -events will be sent to __dycall. In the case of global events, all -global events will trigger this method. In the case of behaviors, all undefined -dynamic events which are called will be passed through to this method. -

-

-

Behaviors

-

-There are two types of behaviors. There are individual object behaviors and -there are class wide behaviors. Class behaviors depend upon object behaviors. -

-

-When a new class implements IBehavior or IClassBehavior or -extends TBehavior or TClassBehavior, it may be added to an -object by calling the object's attachBehavior. The behaviors associated -name can then be used to enableBehavior or disableBehavior -the specific behavior. -

-

-All behaviors may be turned on and off via enableBehaviors and -disableBehaviors, respectively. To check if behaviors are on or off -a call to getBehaviorsEnabled will provide the variable. -

-

-Attaching and detaching whole sets of behaviors is done using -attachBehaviors and detachBehaviors. clearBehaviors -removes all of an object's behaviors. -

-

-asa returns a behavior of a specific name. isa is the -behavior inclusive function that acts as the PHP operator instanceof. -A behavior could provide the functionality of a specific class thus causing -the host object to act similarly to a completely different class. A behavior -would then implement IInstanceCheck to provide the identity of the -different class. -

-

-Class behaviors are similar to object behaviors except that the class behavior -is the implementation for all instances of the class. A class behavior -will have the object upon which is being called be prepended to the parameter -list. This way the object is known across the class behavior implementation. -

-

-Class behaviors are attached using attachClassBehavior and detached -using detachClassBehavior. Class behaviors are important in that -they will be applied to all new instances of a particular class. In this way -class behaviors become default behaviors to a new instances of a class in -__construct. Detaching a class behavior will remove the behavior -from the default set of behaviors created for an object when the object -is instanced. -

-

-Class behaviors are also added to all existing instances via the global 'fx' -event mechanism. When a new class behavior is added, the event -fxAttachClassBehavior is raised and all existing instances that are -listening to this global event (primarily after listen is called) -will have this new behavior attached. A similar process is used when -detaching class behaviors. Any objects listening to the global 'fx' event -fxDetachClassBehavior will have a class behavior removed. -

-

Dynamic Intra-Object Events

-

-Dynamic events start with 'dy'. This mechanism is used to allow objects -to communicate with their behaviors directly. The entire 'dy' event space -is valid. All attached, enabled behaviors that implement a dynamic event -are called when the host object calls the dynamic event. If there is no -implementation or behaviors, this returns null when no parameters are -supplied and will return the first parameter when there is at least one -parameter in the dynamic event. -

- - null == $this->dyBehaviorEvent(); - 5 == $this->dyBehaviorEvent(5); //when no behaviors implement this dynamic event - -

-Dynamic events can be chained together within behaviors to allow for data -filtering. Dynamic events are implemented within behaviors by defining the -event as a method. -

- -class TObjectBehavior extends TBehavior { - public function dyBehaviorEvent($param1, $callchain) { - //Do something, eg: $param1 += 13; - return $callchain->dyBehaviorEvent($param1); - } -} - -

-This implementation of a behavior and dynamic event will flow through to the -next behavior implementing the dynamic event. The first parameter is always -return when it is supplied. Otherwise a dynamic event returns null. -

-

-In the case of a class behavior, the object is also prepended to the dynamic -event. -

- -class TObjectClassBehavior extends TClassBehavior { - public function dyBehaviorEvent($hostobject, $param1, $callchain) { - //Do something, eg: $param1 += $hostobject->getNumber(); - return $callchain->dyBehaviorEvent($param1); - } -} - -

-

-When calling a dynamic event, only the parameters are passed. The host object -and the call chain are built into the framework. -

- -

Global Event and Dynamic event catching

- -

-Given that all global 'fx' events and dynamic 'dy' events are valid and -operational, there is a mechanism for catching events called that are not -implemented (similar to the built-in PHP method __call). When -a dynamic or global event is called but a behavior does not implement it, -yet desires to know when an undefined dynamic event is run, the behavior -implements the interface IDynamicMethods and method __dycall. -

-

-In the case of dynamic events, __dycall is supplied with the method -name and its parameters. When a global event is raised, via raiseEvent, -the method is the event name and the parameters are supplied. -

-

-When implemented, this catch-all mechanism is called for event global event event -when implemented outside of a behavior. Within a behavior, it will also be called -when the object to which the behavior is attached calls any unimplemented dynamic -event. This is the fall-back mechanism for informing a class and/or behavior -of when an global and/or undefined dynamic event is executed. -

- -

Namespaces

-

-A namespace refers to a logical grouping of some class names so that they can be differentiated from other class names even if their names are the same. Since PHP does not support namespace intrinsically, you cannot create instances of two classes who have the same name but with different definitions. To differentiate from user defined classes, all PRADO classes are prefixed with a letter 'T' (meaning 'Type'). Users are advised not to name their classes like this. Instead, they may prefix their class names with any other letter(s). -

-

-A namespace in PRADO is considered as a directory containing one or several class files. A class may be specified without ambiguity using such a namespace followed by the class name. Each namespace in PRADO is specified in the following format, -

-PathAlias.Dir1.Dir2 -
-where PathAlias is an alias of some directory, while Dir1 and Dir2 are subdirectories under that directory. A class named MyClass defined under Dir2 may now be fully qualified as PathAlias.Dir1.Dir2.MyClass. -

-

-To use a namespace in code, do as follows, - -Prado::using('PathAlias.Dir1.Dir2.*'); - -which appends the directory referred to by PathAlias.Dir1.Dir2 into PHP include path so that classes defined under that directory may be instantiated without the namespace prefix. You may also include an individual class definition by - -Prado::using('PathAlias.Dir1.Dir2.MyClass'); - -which will include the class file if MyClass is not defined. -

-

-For more details about defining path aliases, see application configuration section. -

- -

Component Instantiation

-

-Component instantiation means creating instances of component classes. There are two types of component instantation: static instantiation and dynamic instantiation. The created components are called static components and dynamic components, respectively. -

- -

Dynamic Component Instantiation

-

-Dynamic component instantiation means creating component instances in PHP code. It is the same as the commonly referred object creation in PHP. A component can be dynamically created using one of the following two methods in PHP, - -$component = new ComponentClassName; -$component = Prado::createComponent('ComponentType'); - -where ComponentType refers to a class name or a type name in namespace format (e.g. System.Web.UI.TControl). The second approach is introduced to compensate for the lack of namespace support in PHP. -

- -

Static Component Instantiation

-

-Static component instantiation is about creating components via configurations. The actual creation work is done by the PRADO framework. For example, in an application configuration, one can configure a module to be loaded when the application runs. The module is thus a static component created by the framework. Static component instantiation is more commonly used in templates. Every component tag in a template specifies a component that will be automatically created by the framework when the template is loaded. For example, in a page template, the following tag will lead to the creation of a TButton component on the page, - -<com:TButton Text="Register" /> - -

- -
diff --git a/demos/quickstart/protected/pages/Fundamentals/Components1.page b/demos/quickstart/protected/pages/Fundamentals/Components1.page new file mode 100755 index 00000000..a610045d --- /dev/null +++ b/demos/quickstart/protected/pages/Fundamentals/Components1.page @@ -0,0 +1,157 @@ + +

Components: Part I

+

+A component is an instance of TComponent or its child class. The base class TComponent implements the mechanism of component properties and events. +

+ +

Component Properties

+

+A component property can be viewed as a public variable describing a specific aspect of the component, such as the background color, the font size, etc. A property is defined by the existence of a getter and/or a setter method in the component class. For example, in TControl, we define its ID property using the following getter and setter methods, + +class TControl extends TComponent { + public function getID() { + ... + } + public function setID($value) { + ... + } +} + +

+

+To get or set the ID property, do as follows, just like working with a variable, + +$id = $component->ID; +$component->ID = $id; + +This is equivalent to the following, + +$id = $component->getID(); +$component->setID( $id ); + +

+

+A property is read-only if it has a getter method but no setter method. Since PHP method names are case-insensitive, property names are also case-insensitive. A component class inherits all its ancestor classes' properties. +

+ +

Subproperties

+

+A subproperty is a property of some object-typed property. For example, TWebControl has a Font property which is of TFont type. Then the Name property of Font is referred to as a subproperty (with respect to TWebControl). +

+

+To get or set the Name subproperty, use the following method, + +$name = $component->getSubProperty('Font.Name'); +$component->setSubProperty('Font.Name', $name); + +This is equivalent to the following, + +$name = $component->getFont()->getName(); +$component->getFont()->setName( $name ); + +

+ +

Js-friendly properties

+

+A JavaScript-friendly property is a property that can accept both simple strings and raw javascript. +Prado automatically encodes all properties sent clientside inside javascript blocks to avoid security problems (like injections or cross site scripting). +If a property is known to always contain only safe javascript code and its value needs to bypass this encoding, that property can be defined in a special way that will make Prado mark its value as "safe". +Js-friendly properties are identified by their name starting with 'js' (case insensitive): + +// getter, defines a readable property 'Text' +function getJsText() { … } +// setter, defines a writable property 'Text', with $value being the value to be set to the property +function setJsText(TJavaScriptLiteral $value) { … } + +Js-friendly properties can be accessed using both their Js-less name and their Js-enabled name: + +// set some simple text as property value +$component->Text = 'text'; +// set some javascript code as property value +$component->JsText = 'raw javascript'; + +In the first case, the property value will automatically gets encoded when sent clientside inside a javascript block. +In the second case, the property will be 'marked' as being a safe javascript statement and will not be encoded when rendered inside a javascript block. +This special handling makes use of the TJavaScriptLiteral class. +

+ + +

Component Events

+

+Component events are special properties that take method names as their values. Attaching (setting) a method to an event will hook up the method to the places at which the event is raised. Therefore, the behavior of a component can be modified in a way that may not be foreseen during the development of the component. +

+

+A component event is defined by the existence of a method whose name starts with the word on. The event name is the method name and is thus case-insensitve. For example, in TButton, we have + +class TButton extends TWebControl { + public function onClick( $param ) { + ... + } +} + +This defines an event named OnClick, and a handler can be attached to the event using one of the following ways, + +$button->OnClick = $callback; +$button->OnClick->add( $callback ); +$button->OnClick[] = $callback; +$button->attachEventHandler( 'OnClick' , $callback ); + +

+ The variable $callback contains the definition of the event handler that can be either a string referring to a global function name, or an array whose first element refers to an object and second element a method name/path that is reachable by the object, e.g. +

+ + +

Namespaces

+

+A namespace refers to a logical grouping of some class names so that they can be differentiated from other class names even if their names are the same. Since PHP does not support namespace intrinsically, you cannot create instances of two classes who have the same name but with different definitions. To differentiate from user defined classes, all PRADO classes are prefixed with a letter 'T' (meaning 'Type'). Users are advised not to name their classes like this. Instead, they may prefix their class names with any other letter(s). +

+

+A namespace in PRADO is considered as a directory containing one or several class files. A class may be specified without ambiguity using such a namespace followed by the class name. Each namespace in PRADO is specified in the following format, +

+PathAlias.Dir1.Dir2 +
+where PathAlias is an alias of some directory, while Dir1 and Dir2 are subdirectories under that directory. A class named MyClass defined under Dir2 may now be fully qualified as PathAlias.Dir1.Dir2.MyClass. +

+

+To use a namespace in code, do as follows, + +Prado::using('PathAlias.Dir1.Dir2.*'); + +which appends the directory referred to by PathAlias.Dir1.Dir2 into PHP include path so that classes defined under that directory may be instantiated without the namespace prefix. You may also include an individual class definition by + +Prado::using('PathAlias.Dir1.Dir2.MyClass'); + +which will include the class file if MyClass is not defined. +

+

+For more details about defining path aliases, see application configuration section. +

+ +

Component Instantiation

+

+Component instantiation means creating instances of component classes. There are two types of component instantation: static instantiation and dynamic instantiation. The created components are called static components and dynamic components, respectively. +

+ +

Dynamic Component Instantiation

+

+Dynamic component instantiation means creating component instances in PHP code. It is the same as the commonly referred object creation in PHP. A component can be dynamically created using one of the following two methods in PHP, + +$component = new ComponentClassName; +$component = Prado::createComponent('ComponentType'); + +where ComponentType refers to a class name or a type name in namespace format (e.g. System.Web.UI.TControl). The second approach is introduced to compensate for the lack of namespace support in PHP. +

+ +

Static Component Instantiation

+

+Static component instantiation is about creating components via configurations. The actual creation work is done by the PRADO framework. For example, in an application configuration, one can configure a module to be loaded when the application runs. The module is thus a static component created by the framework. Static component instantiation is more commonly used in templates. Every component tag in a template specifies a component that will be automatically created by the framework when the template is loaded. For example, in a page template, the following tag will lead to the creation of a TButton component on the page, + +<com:TButton Text="Register" /> + +

+ +
diff --git a/demos/quickstart/protected/pages/Fundamentals/Components2.page b/demos/quickstart/protected/pages/Fundamentals/Components2.page new file mode 100644 index 00000000..7fc3e010 --- /dev/null +++ b/demos/quickstart/protected/pages/Fundamentals/Components2.page @@ -0,0 +1,207 @@ + +

Components: Part II

+ +

Global events

+

+With the addition of behaviors, a more expansive event model is needed. There +are two new event types (global and dynamic events) as well as a more comprehensive +behavior model that includes class wide behaviors. +

+

+A global event is defined by all events whose name starts with 'fx'. +The event name is potentially a method name and is thus case-insensitive. All 'fx' events +are valid as the whole 'fx' event/method space is global in nature. Any object may patch into +any global event by defining that event as a method. Global events have priorities +just like 'on' events; so as to be able to order the event execution. Due to the +nature of all events which start with 'fx' being valid, in effect, every object +has every 'fx' global event. It is simply an issue of tapping into the desired +global event. +

+

+A global event that starts with 'fx' can be called even if the object does not +implement the method of the global event. A call to a non-existing 'fx' method +will, at minimal, function and return null. If a method argument list has a first +parameter, it will be returned instead of null. This allows filtering and chaining. +'fx' methods do not automatically install and uninstall. To install and uninstall an +object's global event listeners, call the object's listen and +unlisten methods, respectively. An object may auto-install its global event +during __construct by overriding getAutoGlobalListen and returning true. +

+

+As of PHP version 5.3, nulled objects without code references will still continue to persist +in the global event queue because __destruct is not automatically called. In the common +__destruct method, if an object is listening to global events, then unlisten is called. +unlisten is required to be manually called before an object is +left without references if it is currently listening to any global events. This includes +class wide behaviors. +

+

+An object that contains a method that starts with 'fx' will have those functions +automatically receive those events of the same name after listen is called on the object. +

+

+An object may listen to a global event without defining an 'fx' method of the same name by +adding an object method to the global event list. For example +

+ +$component->fxGlobalCheck=$callback; // or $component->OnClick->add($callback); +$component->attachEventHandler('fxGlobalCheck',array($object, 'someMethod')); + +

Events between Objects and their behaviors, Dynamic Events

+

+An intra-object/behavior event is defined by methods that start with 'dy'. Just as with +'fx' global events, every object has every dynamic event. Any call to a method that +starts with 'dy' will be handled, regardless of whether it is implemented. These +events are for communicating with attached behaviors. +

+

+Dynamic events can be used in a variety of ways. They can be used to tell behaviors +when a non-behavior method is called. Dynamic events could be used as data filters. +They could also be used to specify when a piece of code is to be run, eg. should the +loop process be performed on a particular piece of data. In this way, some control +is handed to the behaviors over the process and/or data. +

+

+If there are no handlers for an 'fx' or 'dy' event, it will return the first +parameter of the argument list. If there are no arguments, these events +will return null. If there are handlers an 'fx' method will be called directly +within the object. Global 'fx' events are triggered by calling raiseEvent. +For dynamic events where there are behaviors that respond to the dynamic events, a +TCallChain is developed. A call chain allows the behavior dynamic event +implementations to call further implementing behaviors within a chain. +

+

+If an object implements IDynamicMethods, all global and object dynamic +events will be sent to __dycall. In the case of global events, all +global events will trigger this method. In the case of behaviors, all undefined +dynamic events which are called will be passed through to this method. +

+

+

Behaviors

+

+There are two types of behaviors. There are individual object behaviors and +there are class wide behaviors. Class behaviors depend upon object behaviors. +

+

+When a new class implements IBehavior or IClassBehavior or +extends TBehavior or TClassBehavior, it may be added to an +object by calling the object's attachBehavior. The behaviors associated +name can then be used to enableBehavior or disableBehavior +the specific behavior. +

+

+All behaviors may be turned on and off via enableBehaviors and +disableBehaviors, respectively. To check if behaviors are on or off +a call to getBehaviorsEnabled will provide the variable. +

+

+Attaching and detaching whole sets of behaviors is done using +attachBehaviors and detachBehaviors. clearBehaviors +removes all of an object's behaviors. +

+

+asa returns a behavior of a specific name. isa is the +behavior inclusive function that acts as the PHP operator instanceof. +A behavior could provide the functionality of a specific class thus causing +the host object to act similarly to a completely different class. A behavior +would then implement IInstanceCheck to provide the identity of the +different class. +

+

+Class behaviors are similar to object behaviors except that the class behavior +is the implementation for all instances of the class. A class behavior +will have the object upon which is being called be prepended to the parameter +list. This way the object is known across the class behavior implementation. +

+

+Class behaviors are attached using attachClassBehavior and detached +using detachClassBehavior. Class behaviors are important in that +they will be applied to all new instances of a particular class. In this way +class behaviors become default behaviors to a new instances of a class in +__construct. Detaching a class behavior will remove the behavior +from the default set of behaviors created for an object when the object +is instanced. +

+

+Class behaviors are also added to all existing instances via the global 'fx' +event mechanism. When a new class behavior is added, the event +fxAttachClassBehavior is raised and all existing instances that are +listening to this global event (primarily after listen is called) +will have this new behavior attached. A similar process is used when +detaching class behaviors. Any objects listening to the global 'fx' event +fxDetachClassBehavior will have a class behavior removed. +

+

Dynamic Intra-Object Events

+

+Dynamic events start with 'dy'. This mechanism is used to allow objects +to communicate with their behaviors directly. The entire 'dy' event space +is valid. All attached, enabled behaviors that implement a dynamic event +are called when the host object calls the dynamic event. If there is no +implementation or behaviors, this returns null when no parameters are +supplied and will return the first parameter when there is at least one +parameter in the dynamic event. +

+ + null == $this->dyBehaviorEvent(); + 5 == $this->dyBehaviorEvent(5); //when no behaviors implement this dynamic event + +

+Dynamic events can be chained together within behaviors to allow for data +filtering. Dynamic events are implemented within behaviors by defining the +event as a method. +

+ +class TObjectBehavior extends TBehavior { + public function dyBehaviorEvent($param1, $callchain) { + //Do something, eg: $param1 += 13; + return $callchain->dyBehaviorEvent($param1); + } +} + +

+This implementation of a behavior and dynamic event will flow through to the +next behavior implementing the dynamic event. The first parameter is always +return when it is supplied. Otherwise a dynamic event returns null. +

+

+In the case of a class behavior, the object is also prepended to the dynamic +event. +

+ +class TObjectClassBehavior extends TClassBehavior { + public function dyBehaviorEvent($hostobject, $param1, $callchain) { + //Do something, eg: $param1 += $hostobject->getNumber(); + return $callchain->dyBehaviorEvent($param1); + } +} + +

+

+When calling a dynamic event, only the parameters are passed. The host object +and the call chain are built into the framework. +

+ +

Global Event and Dynamic event catching

+ +

+Given that all global 'fx' events and dynamic 'dy' events are valid and +operational, there is a mechanism for catching events called that are not +implemented (similar to the built-in PHP method __call). When +a dynamic or global event is called but a behavior does not implement it, +yet desires to know when an undefined dynamic event is run, the behavior +implements the interface IDynamicMethods and method __dycall. +

+

+In the case of dynamic events, __dycall is supplied with the method +name and its parameters. When a global event is raised, via raiseEvent, +the method is the event name and the parameters are supplied. +

+

+When implemented, this catch-all mechanism is called for event global event event +when implemented outside of a behavior. Within a behavior, it will also be called +when the object to which the behavior is attached calls any unimplemented dynamic +event. This is the fall-back mechanism for informing a class and/or behavior +of when an global and/or undefined dynamic event is executed. +

+ +
diff --git a/demos/quickstart/protected/pages/Fundamentals/Pages.page b/demos/quickstart/protected/pages/Fundamentals/Pages.page index 19900937..0f3a8d9f 100755 --- a/demos/quickstart/protected/pages/Fundamentals/Pages.page +++ b/demos/quickstart/protected/pages/Fundamentals/Pages.page @@ -5,7 +5,7 @@ Pages are top-most controls that have no parent. The presentation of pages are directly displayed to end-users. Users access pages by sending page service requests.

-Each page must have a template file. The file name suffix must be .page. The file name (without suffix) is the page name. PRADO will try to locate a page class file under the directory containing the page template file. Such a page class file must have the same file name (suffixed with .php) as the template file. If the class file is not found, the page will take class TPage. +Each page can have a template file. The file name suffix must be .page. The file name (without suffix) is the page name. PRADO will try to locate a page class file under the directory containing the page template file. Such a page class file must have the same file name (suffixed with .php) as the template file. If the class file is not found, the page will take class TPage.

PostBack

@@ -14,6 +14,18 @@ A form submission is called postback if the submission is made to the pag

+

CallBack

+

+A callback is a special form submission that, instead of requiring a full page reload on the browser, gets executed in the background through an ajax call. So, a callback is considered a postback too, but not vice versa. +
+A callback is handled as a normal postback but, instead of re-rendering the entire page, only the specific changes occured on the page gets sent back to the client and merged with the current browser page. A typical callback response consists of: +

    +
  1. one or more pieces of html code that will replace existing content on the page;
  2. +
  3. the javascript instructions needed to update the page;
  4. +
  5. some specific fields used by prado to mantain the pagestate and add the needed external resources (stylesheets, javascript files, etc..).
  6. +
+

+

Page Lifecycles

Understanding the page lifecycles is crucial to grasp PRADO programming. Page lifecycles refer to the state transitions of a page when serving this page to end-users. They can be depicted in the following statechart, diff --git a/demos/quickstart/protected/pages/Fundamentals/Services.page b/demos/quickstart/protected/pages/Fundamentals/Services.page index 5e5889df..ee19a159 100755 --- a/demos/quickstart/protected/pages/Fundamentals/Services.page +++ b/demos/quickstart/protected/pages/Fundamentals/Services.page @@ -16,7 +16,7 @@ Developers may implement additional services for their applications. To make a s

Page Service

-PRADO implements TPageService to process users' page requests. Pages are stored under a directory specified by the BasePath property of the page service. The property defaults to pages directory under the application base path. You may change this default by configuring the service in the application configuration. +PRADO implements TPageService to process users' page requests. Pages are stored under a directory specified by the BasePath property of the page service. The property defaults to Pages directory under the application base path. You may change this default by configuring the service in the application configuration.

Pages may be organized into subdirectories under the BasePath. In each directory, there may be a page configuration file named config.xml, which contains configurations effective only when a page under that directory or a sub-directory is requested. For more details, see the page configuration section. @@ -30,5 +30,8 @@ http://hostname/index.php?page=Users.Register where the first example takes advantage of the fact that the page service is the default service and Home is the default page.

+

+More advanced url routes, like masking real page names and permitting the use of dynamic parameters can be created using the Url mapping module. +

-- cgit v1.2.3 From 1d729693961dfa4cf4da45a05d703b392dbcb47f Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 12 Jan 2014 23:45:18 +0100 Subject: Quickstart Doc overhaul, pt. 2: active controls + minor fixes --- demos/quickstart/protected/controls/TopicList.tpl | 1 + .../quickstart/protected/controls/es/TopicList.tpl | 4 +- .../quickstart/protected/controls/fr/TopicList.tpl | 2 +- .../quickstart/protected/controls/id/TopicList.tpl | 2 +- .../quickstart/protected/controls/ja/TopicList.tpl | 2 +- .../quickstart/protected/controls/pl/TopicList.tpl | 3 +- .../quickstart/protected/controls/zh/TopicList.tpl | 2 +- .../pages/ActiveControls/ActiveButton.page | 66 ------- .../protected/pages/ActiveControls/Home.page | 91 +-------- .../pages/ActiveControls/Introduction.page | 203 ++++++++++++++++++++- .../quickstart/protected/pages/Advanced/Error.page | 2 +- .../protected/pages/Controls/NewControl.page | 2 +- .../protected/pages/Controls/id/NewControl.page | 2 +- .../protected/pages/Database/ActiveRecord.page | 6 +- .../protected/pages/Database/id/ActiveRecord.page | 140 +++++++------- .../protected/pages/Database/pl/ActiveRecord.page | 66 +++---- .../protected/pages/Fundamentals/Pages.page | 4 +- .../pages/GettingStarted/CommandLine.page | 35 +++- 18 files changed, 356 insertions(+), 277 deletions(-) (limited to 'demos/quickstart/protected/pages/Fundamentals') diff --git a/demos/quickstart/protected/controls/TopicList.tpl b/demos/quickstart/protected/controls/TopicList.tpl index 586b7f75..dbb744b6 100755 --- a/demos/quickstart/protected/controls/TopicList.tpl +++ b/demos/quickstart/protected/controls/TopicList.tpl @@ -56,6 +56,7 @@
  • Validation Controls
  • List Controls
  • Data Controls
  • +
  • AJAX: Introduction
  • Active Controls (AJAX)
  • Writing New Controls
  • diff --git a/demos/quickstart/protected/controls/es/TopicList.tpl b/demos/quickstart/protected/controls/es/TopicList.tpl index be4be752..41359ac2 100755 --- a/demos/quickstart/protected/controls/es/TopicList.tpl +++ b/demos/quickstart/protected/controls/es/TopicList.tpl @@ -1,7 +1,7 @@
    -
    Comenzar
    +
    Comenzar
    • Introducción
    • ¿Que es PRADO?
    • @@ -27,7 +27,7 @@
      Básicos
      • Arquitectura
      • -
      • Componentes
      • +
      • Componentes
      • Controles
      • Páginas
      • Mádulos
      • diff --git a/demos/quickstart/protected/controls/fr/TopicList.tpl b/demos/quickstart/protected/controls/fr/TopicList.tpl index 44bb0b16..c2de4a9a 100755 --- a/demos/quickstart/protected/controls/fr/TopicList.tpl +++ b/demos/quickstart/protected/controls/fr/TopicList.tpl @@ -26,7 +26,7 @@
        Fundamentaux
        • Architecture
        • -
        • Composants
        • +
        • Composants
        • Contrôles
        • Pages
        • Modules
        • diff --git a/demos/quickstart/protected/controls/id/TopicList.tpl b/demos/quickstart/protected/controls/id/TopicList.tpl index bfc6ed64..fa00ee37 100755 --- a/demos/quickstart/protected/controls/id/TopicList.tpl +++ b/demos/quickstart/protected/controls/id/TopicList.tpl @@ -27,7 +27,7 @@
          Fundamental
          • Arsitektur
          • -
          • Komponen
          • +
          • Komponen
          • Kontrol
          • Halaman
          • Modul
          • diff --git a/demos/quickstart/protected/controls/ja/TopicList.tpl b/demos/quickstart/protected/controls/ja/TopicList.tpl index ccaf5c63..bfdbf129 100755 --- a/demos/quickstart/protected/controls/ja/TopicList.tpl +++ b/demos/quickstart/protected/controls/ja/TopicList.tpl @@ -27,7 +27,7 @@
            動作原理
            @@ -27,7 +26,7 @@
            Postawy
            • Architektura
            • -
            • Komponenty
            • +
            • Komponenty
            • Kontrolki
            • Strony
            • Moduły
            • diff --git a/demos/quickstart/protected/controls/zh/TopicList.tpl b/demos/quickstart/protected/controls/zh/TopicList.tpl index 00fa2280..6557a992 100755 --- a/demos/quickstart/protected/controls/zh/TopicList.tpl +++ b/demos/quickstart/protected/controls/zh/TopicList.tpl @@ -26,7 +26,7 @@
              基础概念
              • 框架结构
              • -
              • 部件
              • +
              • 部件
              • 控件
              • 页面
              • 模块
              • diff --git a/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page b/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page index d4167f6e..6385b658 100755 --- a/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page +++ b/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page @@ -24,70 +24,4 @@ events of an TActiveButton.

                -

                TActiveButton Class Diagram

                -

                The class diagram for TActiveButton is illustrated in the figure below. -Most active control that can perform callback request have a similar structure. -

                - - class="figure" - alt="TActiveButton class diagram" title="TActiveButton class diagram" /> - -

                TActiveButton is an extension of TButton -and implements two additional interfaces ICallbackEventHandler and -IActiveControl. The TActiveButton contains an instance of -TBaseActiveCallbackControl -available through the ActiveControl property of TActiveButton. -The following example set the callback parameter of the TActiveButton when -a callback request is dispatched. -

                - -<com:TActiveButton - Text="Click Me" - OnCallback="button_callback" - ActiveControl.CallbackParameter="value" /> - -

                In the OnCallback event handler method, the CallbackParameter -is available in the $param object.

                - -public function button_callback($sender, $param) -{ - echo $param->CallbackParameter; //outputs "value" -} - - -

                Adding Client Side Behaviour

                - -

                With in the ActiveControl property is an instance of -TCallbackClientSide available -as a property ClientSide of TActiveButton. -The ClientSide property contains sub-properties, such as RequestTimeOut, -and client-side javascript event handler, such as OnLoading, -that are used by the client-side when making a callback request. -The following example demonstrates the toggling of a "loading" indicator -when the client-side is making a callback request. -

                - - -<com:TClientScript PradoScripts="effects" /> -Loading... - -<com:TActiveButton - Text="Click Me" - OnCallback="button_callback" - ActiveControl.CallbackParameter="value" > - <prop:ClientSide - OnLoading="Element.show('callback_status')" - OnComplete="Element.hide('callback_status')" /> -</com:TActiveButton> - - -

                The example loads the "effects" javascript library using the -TClientScript component. -The ClientSide.OnLoading property value contains -javascript statement that uses the "effects" library to show the "Loading..." -span tag. Similarly, ClientSide.OnComplete property -value contains the javascript statement that hides the "Loading..." span tag. -See TCallbackClientSide for -further details on client-side property details. -

                diff --git a/demos/quickstart/protected/pages/ActiveControls/Home.page b/demos/quickstart/protected/pages/ActiveControls/Home.page index 92bdbca2..4a180620 100755 --- a/demos/quickstart/protected/pages/ActiveControls/Home.page +++ b/demos/quickstart/protected/pages/ActiveControls/Home.page @@ -1,6 +1,7 @@

                Active Controls (AJAX enabled Controls)

                -

                See the Introduction +

                +Active controls extends standard PRADO controls adding the ability to automatically update themselves on callbacks without the need of ad-hoc javascript calls. See the Introduction for a quick overview of the concept behind active controls (AJAX enabled controls). Most active controls have a property of ActiveControl and @@ -373,92 +374,4 @@ if Javascript was disabled on the client's browser.

                -

                Active Control Basic Infrastructure Classes

                -

                The following classes provide the basic infrastructure classes required to -realize the active controls. They can be useful to develop new active controls, but Prado users tipically don't need -to use them.

                -
                  -
                • -

                  TActiveControlAdapter

                  - -

                  - TActiveControlAdapter customizes the parent TControl class for active control classes. - It tracks changes in the viewstate values of the control and update - differences of the client-side HTML element attributes. -

                  -
                • - -
                • -

                  TActiveListControlAdapter

                  - -

                  - TActiveListControlAdapter allows the adapted list controls to change the selections - on the client-side during a callback request. -

                  -
                • - -
                • -

                  TActivePageAdapter

                  - -

                  - TActivePageAdapter process the page life-cycle for callback requests. -

                  -
                • - -
                • -

                  TBaseActiveControl

                  - -

                  - TBaseActiveControl class provided additional basic properties common for every - active control. An instance of TBaseActiveControl or its decendent - TBaseActiveCallbackControl is created by TActiveControlAdapter::getBaseActiveControl() - method. - The EnableUpdate property determines wether the active - control is allowed to update the contents of the client-side when the callback - response returns. -

                  -
                • - -
                • -

                  TCallbackResponseAdapter

                  - -

                  - TCallbackResponseAdapter alters the THttpResponse's outputs. - A TCallbackResponseWriter is used instead of the TTextWrite when - createHtmlWriter is called. Each call to createHtmlWriter will create - a new TCallbackResponseWriter. When flushContent() is called each - instance of TCallbackResponseWriter's content is flushed. - The callback response data can be set using the ResponseData property. -

                  -
                • - -
                - -

                Active Control Infrastructure Advanced Classes

                -

                The following classes provide advanced properties and events needed to realize the active controls. -A Prado user can use them to customize active controls behaviour and interact directly with the client side during a callback. -

                -
                  -
                • - TCallbackClientScript - methods to manipulate the client-side HTML elements, also includes methods - to invoke javascript Effects on HTML elements. -
                • - -
                • - TCallbackClientSide - is used to specify client-side callback request options and client-side event handlers. -
                • - -
                • - TCallbackEventParameter - provides the parameter passed during the callback request. -
                • - -
                • - TCallbackOptions - allows a common set of callback client-side options to be attached to one or more active controls. -
                • -
                -
                diff --git a/demos/quickstart/protected/pages/ActiveControls/Introduction.page b/demos/quickstart/protected/pages/ActiveControls/Introduction.page index 70cb9cbf..edc0b5bc 100755 --- a/demos/quickstart/protected/pages/ActiveControls/Introduction.page +++ b/demos/quickstart/protected/pages/ActiveControls/Introduction.page @@ -1,7 +1,204 @@ -

                Overview of Active Controls

                - -TODO: +

                AJAX: Introduction

                +

                +A classic webpage can only transfer data back to the server using an http postback request that requires a full page reload. This is a problem for web applications, since a synchronous page reload breaks the user interaction: the user must wait for the response to arrive and the page will lose its current status (the scrolling position, the currently focused control, etc..). +

                class="figure" /> +

                +A common solution to this problem is the use of AJAX (Asynchronous JavaScript and XML) callbacks. After the first full page load, the web application can make subsequents requests using javascript. The callback requests are asynchronous, so the user can continue to interact with the page while the response is loading. The response contains a list of changes that will be applied to the page "on the fly", like replacing existing elements with new content or add some css style to an existing element. +

                + +

                Interacting with a page on callback

                + +

                +PRADO has builtin support for AJAX callbacks in the form of Active Controls. These controls can trigger a callback request and have their properties (value, css style, attributes, ..) updated during a callback. +Before digging inside the list of Active Controls, it's good to have a look to how a page can be aware if the current request is a callback and how to interact with the page rendered on the client browser. +
                +The IsCallBack property of the TPage class exposes whether the current request being handled is the consequence of a callback, and the CallbackClient property provides many methods to update and alter the client-side content during a callback request. +

                + + +public function onClick($sender, $param) +{ + if($this->IsCallback) + { + $this->getCallbackClient()->hide($this->TextBox1); + } +} + + +

                Active Controls (AJAX enabled Controls)

                +

                +Active controls extends standard PRADO controls adding the ability to automatically update themselves on callbacks without the need of ad-hoc javascript calls. Active controls are reliant on a collection of javascript classes that gets added to the page automatically when needed. +

                + +

                +Most active controls have a ActiveControl.EnableUpdate property that determines whether the active control is allowed to update the contents of the client-side when the callback response returns. Depending on the control different properties can be updated. +

                + +

                +Some active controls can trigger a callback as a consequence of a clientside event (a button click, a checkbox being checked, a DOM event). The callback will first raise the normal serverside event associated to the control (eg: OnClick for a TButton or OnSelectedIndexChanged for a TRadioButtonList) and then the OnCallBack event. +The AutoPostBack property typically defaults to true for these controls. +

                +

                + +

                +Active controls have a ClientSide property that provides many subproperties to customize the controls and to hook some javascript code to the callback lifecycle, like showing a "Loading" logo at the start of a callback and hide it at the end. +

                + +

                TActiveButton Class Diagram

                +

                The class diagram for TActiveButton is illustrated in the figure below. +Most active control that can perform callback request have a similar structure. +

                + + class="figure" + alt="TActiveButton class diagram" title="TActiveButton class diagram" /> + +

                TActiveButton is an extension of TButton +and implements two additional interfaces ICallbackEventHandler and +IActiveControl. The TActiveButton contains an instance of +TBaseActiveCallbackControl +available through the ActiveControl property of TActiveButton. +The following example set the callback parameter of the TActiveButton when +a callback request is dispatched. +

                + +<com:TActiveButton + Text="Click Me" + OnCallback="button_callback" + ActiveControl.CallbackParameter="value" /> + +

                In the OnCallback event handler method, the CallbackParameter +is available in the $param object.

                + +public function button_callback($sender, $param) +{ + echo $param->CallbackParameter; //outputs "value" +} + + +

                Adding Client Side Behaviour

                + +

                With in the ActiveControl property is an instance of +TCallbackClientSide available +as a property ClientSide of TActiveButton. +The ClientSide property contains sub-properties, such as RequestTimeOut, +and client-side javascript event handler, such as OnLoading, +that are used by the client-side when making a callback request. +The following example demonstrates the toggling of a "loading" indicator +when the client-side is making a callback request. +

                + + +<com:TClientScript PradoScripts="effects" /> +Loading... + +<com:TActiveButton + Text="Click Me" + OnCallback="button_callback" + ActiveControl.CallbackParameter="value" > + <prop:ClientSide + OnLoading="Element.show('callback_status')" + OnComplete="Element.hide('callback_status')" /> +</com:TActiveButton> + + +

                The example loads the "effects" javascript library using the +TClientScript component. +The ClientSide.OnLoading property value contains +javascript statement that uses the "effects" library to show the "Loading..." +span tag. Similarly, ClientSide.OnComplete property +value contains the javascript statement that hides the "Loading..." span tag. +See TCallbackClientSide for +further details on client-side property details. +

                + +

                Active Control Basic Infrastructure Classes

                +

                The following classes provide the basic infrastructure classes required to +realize the active controls. They can be useful to develop new active controls, but Prado users tipically don't need +to use them.

                +
                  +
                • +

                  TActiveControlAdapter

                  + +

                  + TActiveControlAdapter customizes the parent TControl class for active control classes. + It tracks changes in the viewstate values of the control and update + differences of the client-side HTML element attributes. +

                  +
                • + +
                • +

                  TActiveListControlAdapter

                  + +

                  + TActiveListControlAdapter allows the adapted list controls to change the selections + on the client-side during a callback request. +

                  +
                • + +
                • +

                  TActivePageAdapter

                  + +

                  + TActivePageAdapter process the page life-cycle for callback requests. +

                  +
                • + +
                • +

                  TBaseActiveControl

                  + +

                  + TBaseActiveControl class provided additional basic properties common for every + active control. An instance of TBaseActiveControl or its decendent + TBaseActiveCallbackControl is created by TActiveControlAdapter::getBaseActiveControl() + method. + The EnableUpdate property determines wether the active + control is allowed to update the contents of the client-side when the callback + response returns. +

                  +
                • + +
                • +

                  TCallbackResponseAdapter

                  + +

                  + TCallbackResponseAdapter alters the THttpResponse's outputs. + A TCallbackResponseWriter is used instead of the TTextWrite when + createHtmlWriter is called. Each call to createHtmlWriter will create + a new TCallbackResponseWriter. When flushContent() is called each + instance of TCallbackResponseWriter's content is flushed. + The callback response data can be set using the ResponseData property. +

                  +
                • + +
                + +

                Active Control Infrastructure Advanced Classes

                +

                The following classes provide advanced properties and events needed to realize the active controls. +A Prado user can use them to customize active controls behaviour and interact directly with the client side during a callback. +

                +
                  +
                • + TCallbackClientScript + methods to manipulate the client-side HTML elements, also includes methods + to invoke javascript Effects on HTML elements. +
                • + +
                • + TCallbackClientSide + is used to specify client-side callback request options and client-side event handlers. +
                • + +
                • + TCallbackEventParameter + provides the parameter passed during the callback request. +
                • + +
                • + TCallbackOptions + allows a common set of callback client-side options to be attached to one or more active controls. +
                • +
                diff --git a/demos/quickstart/protected/pages/Advanced/Error.page b/demos/quickstart/protected/pages/Advanced/Error.page index 24c7ead0..391997fb 100755 --- a/demos/quickstart/protected/pages/Advanced/Error.page +++ b/demos/quickstart/protected/pages/Advanced/Error.page @@ -71,7 +71,7 @@ The naming convention for the template files used for all other exceptions is as Again, if the preferred language is not found, PRADO will try to use exception.html, instead.

                -CAUTION: When saving a template file, please make sure the file is saved using UTF-8 encoding. On Windows, you may use Notepad.exe to accomplish such saving. +CAUTION: When saving a template file, please make sure the file is saved using UTF-8 encoding. On Windows, you may use Notepad++ to accomplish such saving.
                diff --git a/demos/quickstart/protected/pages/Controls/NewControl.page b/demos/quickstart/protected/pages/Controls/NewControl.page index 10c789db..9a680f43 100755 --- a/demos/quickstart/protected/pages/Controls/NewControl.page +++ b/demos/quickstart/protected/pages/Controls/NewControl.page @@ -10,7 +10,7 @@ In general, there are two ways of writing new controls: composition of existing

                Composition of Existing Controls

                -Composition is the easiest way of creating new controls. It mainly involves instantiating existing controls, configuring them and making them the constituent components. The properties of the constituent components are exposed through subproperties. +Composition is the easiest way of creating new controls. It mainly involves instantiating existing controls, configuring them and making them the constituent components. The properties of the constituent components are exposed through subproperties.

                One can compose a new control in two ways. One is to extend TCompositeControl and override the TControl::createChildControls() method. The other is to extend TTemplateControl (or its child classes) and write a control template. The latter is easier to use and can organize the layout constituent components more intuitively, while the former is more efficient because it does not require parsing of the template. diff --git a/demos/quickstart/protected/pages/Controls/id/NewControl.page b/demos/quickstart/protected/pages/Controls/id/NewControl.page index ea083f6c..85c6eff0 100755 --- a/demos/quickstart/protected/pages/Controls/id/NewControl.page +++ b/demos/quickstart/protected/pages/Controls/id/NewControl.page @@ -10,7 +10,7 @@ Secara umum, ada dua cara penulisan kontrol baru: komposisi kontrol yang sudah a

                Komposisi Kontrol yang Sudah Ada

                -Komposisi adalah cara termudah membuat kontrol baru. Ia melibatkan terutama penurunan kontrol yang sudah ada, mengkonfigurasinya dan menjadikannya unsur komponen. Properti dari unsur komponen diperlihatkan melalui subproperti. +Komposisi adalah cara termudah membuat kontrol baru. Ia melibatkan terutama penurunan kontrol yang sudah ada, mengkonfigurasinya dan menjadikannya unsur komponen. Properti dari unsur komponen diperlihatkan melalui subproperti.

                Seseorang dapat menciptakan sebuah kontrol baru dalam dua cara. Pertama adalah memperluas TCompositeControl dan mengganti metode TControl::createChildControls(). Kedua adalah memperluas TTemplateControl (atau kelas anaknya) dan menulis template kontrol. Yang terakhir lebih mudah digunakan dan bisa mengatur tata letak unsur komponen lebih intuitif, sementara pembentuk lebih efisien karena ia tidak perlu menguraikan template. diff --git a/demos/quickstart/protected/pages/Database/ActiveRecord.page b/demos/quickstart/protected/pages/Database/ActiveRecord.page index 537c65e0..cf1485bd 100755 --- a/demos/quickstart/protected/pages/Database/ActiveRecord.page +++ b/demos/quickstart/protected/pages/Database/ActiveRecord.page @@ -129,7 +129,7 @@ You may specify qualified table names. E.g. for MySQL, TABLE = "`database1`.

    Note: -Since version 3.1.3 you can also use a method table() to define the table name. +Since version 3.1.3 you can also use a method table() to define the table name. This allows you to dynamically specify which table should be used by the ActiveRecord. class TeamRecord extends TActiveRecord @@ -159,7 +159,7 @@ class UserRecord extends TActiveRecord { } } -

    More details regarding TComponent can be found in the Components documentation. +

    More details regarding TComponent can be found in the Components documentation. Later we shall use the getter/setters to allow for lazy loading of relationship objects.

    @@ -1128,7 +1128,7 @@ class PlayerRecord extends BaseFkRecord

    We first need to change the $skills=array() declaration to a private property $_skills (notice the underscore) and set it to null instead. This allows us to define the skills property using getter/setter methods -(see Components for details). The getSkills() +(see Components for details). The getSkills() getter method for the skills property will lazy load the corresponding skills foreign record when it is used as follows. Notice that we only do a lazy load when its $player_id is not null (that is, when the record is already fetched from the database or player id was already set). diff --git a/demos/quickstart/protected/pages/Database/id/ActiveRecord.page b/demos/quickstart/protected/pages/Database/id/ActiveRecord.page index 454aa3f9..8e81678e 100755 --- a/demos/quickstart/protected/pages/Database/id/ActiveRecord.page +++ b/demos/quickstart/protected/pages/Database/id/ActiveRecord.page @@ -3,7 +3,7 @@

    Rekaman Aktif adalah obyek yang melapisi baris dalam tabel atau view database, melindungi akses database dan menambahkan logika domain pada data tersebut. - Dasar dari Rekaman Aktif adalah kelas bisnis, sebagai contoh, kelas + Dasar dari Rekaman Aktif adalah kelas bisnis, sebagai contoh, kelas Products, yang hampir menyamai struktur rekaman dari tabel database dibawahnya. Setiap Rekaman Aktif akan bertanggung jawab atas penyimpanan dan pengambilan data ke dan dari database.

    @@ -14,22 +14,22 @@

    Kapan Menggunakannya

    -

    Rekaman Aktif adalah pilihan yang baik untuk logika domain yang tidak terlalu rumit, +

    Rekaman Aktif adalah pilihan yang baik untuk logika domain yang tidak terlalu rumit, seperti membuat, membaca, memutakhirkan, dan menghapus. Derivasi dan validasi didasarkan pada satu rekaman yang bekerja denga baik dalam struktur ini. Rekaman Aktif mempunyai kuntungan utama dalam hal kesederhanaan. Mudah untuk membangun Rekaman Aktif, dan mudah untuk dimengerti.

    Akan tetapi, seiring dengan perkembangan logika bisnis Anda dalm hal kompleksitas, Anda akan segera ingin menggunakan hubungan langsung obyek Anda, koleksi, turunan, dan seterusnya. Ini tidak mudah diterapkan ke dalam Rekaman Aktif, dan menambahkannya sedikit demi sedikit menjadi sangat kacau. Argumen lain terhadap Rekaman Aktif adalah kenyataan bahwa ia menyandingkan desin obyek ke desain database. Ini menjadikannya lebih sulit untuk merefraktorisasi karena proyek terus berjalan.

    - -

    Alternatifnya adalah menggunakan Pemeta Data yang yang memisahkan aturan dari obyek bisnis dan bagaimana obyek ini disimpan. + +

    Alternatifnya adalah menggunakan Pemeta Data yang yang memisahkan aturan dari obyek bisnis dan bagaimana obyek ini disimpan. Prado menyediakan pilihan tambahan antara Rekaman Aktif dan - Pemeta Data SqlMap. - Pemeta Data SqlMap bisa dipakai untuk mengambil obyek Rekaman Aktif, hasilnya; obyek Rekaman Aktif ini bisa dipakai untuk memutakhirkan database. + Pemeta Data SqlMap. + Pemeta Data SqlMap bisa dipakai untuk mengambil obyek Rekaman Aktif, hasilnya; obyek Rekaman Aktif ini bisa dipakai untuk memutakhirkan database. "Hubungan" antara Rekaman Aktif dan SqlMap digambarkan dalam diagram berikut. Lebih rinci mengenai Pemeta Data SqlMap dapat ditemukan dalam Manual SqlMap. alt="Rekaman Aktif dan SqlMap DataMapper" id="fig:diagram.png" class="figure"/>

    - +

    Kelas Rekaman Aktif berfungsi untuk melakukan tugas-tugas berikut.

    @@ -41,22 +41,22 @@

    Implikasi Desain

    -Implementasi Prado terhadap Rekaman Aktif tidak memelihara identitas referensial. Setiap obyek diperoleh menggunakan Rekaman Aktif pada data dalam database. Sebagai contoh, jika Anda meminta kustomer tertentu dan mendapatkan kembali obyek Customer, kali berikutnya Anda meminta kustomer itu, Anda akan kembali mendapatkan turunan lain dari obyek Customer. Ini berarti bahwa perbandingan tepat (misalnya menggunakan ===) akan mengembalikan false, sementara perbandingan bebas (misalnya menggunakan ==) akan mengembalikan true jika nilai obyek sama menurut perbandingan bebas. +Implementasi Prado terhadap Rekaman Aktif tidak memelihara identitas referensial. Setiap obyek diperoleh menggunakan Rekaman Aktif pada data dalam database. Sebagai contoh, jika Anda meminta kustomer tertentu dan mendapatkan kembali obyek Customer, kali berikutnya Anda meminta kustomer itu, Anda akan kembali mendapatkan turunan lain dari obyek Customer. Ini berarti bahwa perbandingan tepat (misalnya menggunakan ===) akan mengembalikan false, sementara perbandingan bebas (misalnya menggunakan ==) akan mengembalikan true jika nilai obyek sama menurut perbandingan bebas.

    Implikasi desain ini terkait dengan pertanyaan berikut. -"Anda pikir kustomer sebagai obyek, di mana hanya satu, +"Anda pikir kustomer sebagai obyek, di mana hanya satu, atau Anda pikir obyek yang Anda operasikan sebagai duplikat dari database?" -Pemetaan O/R lain akan mengartikan bahwa hanya ada satu obyek Kustomer dengan custID 100, dan secara literal ia adalah kustomer. -Jika Anda mendapatkan kustomer dan mengubah field-nya, maka Anda sekarang telah mengubah kustomer itu. -"Itu berbatasan dengan: Anda telah mengubah duplikat kustomer ini, tapi bukan pada duplikat itu. +Pemetaan O/R lain akan mengartikan bahwa hanya ada satu obyek Kustomer dengan custID 100, dan secara literal ia adalah kustomer. +Jika Anda mendapatkan kustomer dan mengubah field-nya, maka Anda sekarang telah mengubah kustomer itu. +"Itu berbatasan dengan: Anda telah mengubah duplikat kustomer ini, tapi bukan pada duplikat itu. Dan jika dua orang memutakhirkan kustomer pada dua duplikat obyek, siapapun yang memutakhirkan pertama kali, atau mungkin yang terakhir yang menang." [A. Hejlsberg 2003]

    Database yang Didukung

    -Implementasi Rekaman Aktif memanfaatkan kelas Prado DAO untuk akses data. -Implementasi Rekaman Aktif saat ini mendukung database sebagai berikut. +Implementasi Rekaman Aktif memanfaatkan kelas Prado DAO untuk akses data. +Implementasi Rekaman Aktif saat ini mendukung database sebagai berikut.

    • MySQL 4.1 atau lebih tinggi
    • @@ -69,8 +69,8 @@ Implementasi Rekaman Aktif saat ini mendukung database sebagai berikut.

      Mendefinisikan Rekaman Aktif

      Mari kita anggap tabel - "users" berikut yang berisi dua kolom bernama "username" dan "email", - di mana "username" juga merupakan kunci primer. + "users" berikut yang berisi dua kolom bernama "username" dan "email", + di mana "username" juga merupakan kunci primer. CREATE TABLE users ( @@ -88,7 +88,7 @@ class UserRecord extends TActiveRecord public $username; //kolom bernama "username" dalam tabel "users" public $email; - + /** * @return TActiveRecord active record finder instance */ @@ -116,7 +116,7 @@ Anda dapat menetapkan nama-nama tabel yang memenuhi syarat. Contohnya untuk MySQ class UserRecord extends TActiveRecord { ... //definisi yang sudah ada seprti di atas - + private $_level; public function setLevel($value) { $this->_level=TPropertyValue::ensureInteger($value,0); @@ -126,18 +126,18 @@ class UserRecord extends TActiveRecord { } } -

      Lebih jelas mengenai TComponent dapat ditemukan dalam Dokumentasi komponen. +

      Lebih jelas mengenai TComponent dapat ditemukan dalam Dokumentasi komponen. Nantinya kita harus dapat menggunakan pengambil/penyetel guna membolehkan pengambilan malas atas obyek yang berhubungan.

      Info: TActiveRecord juga dapat bekerja dengan view database dengan menetapkan konstan TABLE terkait ke nama view. Akan tetapi, obyek yang dikembalikan dari view hanya-baca, memanggil metode -save() atau delete() akan memunculkan eksepsi. +save() atau delete() akan memunculkan eksepsi.

      - Metode statis finder() mengembalilkan turunan UserRecord + Metode statis finder() mengembalilkan turunan UserRecord yang dapat dipakai untuk mengambil rekaman dari database. Pengambilan rekaman menggunakan metode finder akan didiskusikan nanti. Metode statis TActiveRecord::finder() mengambil nama kelas Rekaman Aktif sebagai parameter.

      @@ -152,7 +152,7 @@ obyek yang dikembalikan dari view hanya-baca, memanggil metode $dsn = 'pgsql:host=localhost;dbname=test'; //Postgres SQL $conn = new TDbConnection($dsn, 'dbuser','dbpass'); TActiveRecordManager::getInstance()->setDbConnection($conn); - +

      Alternatifnya, Anda dapat membuat basis kelas dan mengganti metode getDbConnection() untuk mengembalikan koneksi database. Ini adalah cara sederhana untuk mengijinkan koneksi database multipel. Kode berikut mendemonstrasikan penetapan koneksi database dalam sebuah basis kelas (tidak perlu menyetel koneksi DB di manapun juga). @@ -178,13 +178,13 @@ class MyDb2Record extends TActiveRecord return $conn; } } - +

      Menggunakan application.xml di dalam Kerangka Kerja Prado

      - Koneksi database standar dapat juga dikonfigurasi menggunakan tag <module> dalam application.xml + Koneksi database standar dapat juga dikonfigurasi menggunakan tag <module> dalam application.xml atau config.xml seperti berikut. @@ -192,8 +192,8 @@ class MyDb2Record extends TActiveRecord - - + +

      Tip: Atribut EnableCache ketika disetel ke "true" akan melakukan cache meta data tabel, yakni nama kolom tabel, indeks dan batasan yang disimpan dalam cache dan dipakai ulang. Anda harus membersihkan atau mematikan cache jika Anda ingin melihat perubahan terhadap definisi tabel Anda. Modul cache juga harus didefinisikan agar cache berfungsi.
      @@ -207,13 +207,13 @@ class MyDb2Record extends TActiveRecord Username="dbuser" Password="dbpass" /> - - - + +

      @@ -299,10 +299,10 @@ $criteria->Offset = 20;
      Catatan: Untuk MSSQL dan saat Limit serta Offset berisi nilai integer positif. Query aktual yang dijalankan diubah oleh kelas - -berdasarkan pada +berdasarkan pada http://troels.arvin.dk/db/rdbms/ untuk mengemulasikan kondisi Limit dan Offset.
      @@ -332,11 +332,11 @@ $finder->find('Username = ? AND Password = ?', $name, $pass); $finder->findAllByAge($age); $finder->findAll('Age = ?', $age); - +
      Tip: Anda juga dapat menggunakan kombinasi AND dan OR sebagai kondisi dalam metode dinamis.
      - +

      findBySql() dan findAllBySql()

      Mencari rekaman menggunakan SQL penuh di mana findBySql() mengembalikan Rekaman Aktif dan findAllBySql()mengembalikan array obyek rekaman. Untuk setiap kolom yang dikembalikan, kelas Rekaman Aktif terkait harus mendefinisikan variabel atau properti untuk setiap nama kolom terkait. @@ -371,7 +371,7 @@ Obyek dimutakhirkan dengan kunci primer dari tabel itu yang berisi definisi yang Sebagai contoh, jika Anda menyisipkan sebuah rekaman baru ke dalam tabel MySQL yang kolomnya didefinisikan dengan "autoincrement", obyek Rekaman Aktif akan dimutakhirkan dengan nilai yang ditambahkan.

    -Untuk memutakhirkan rekaman dalam database, cukup ubah satu atau lebih properti obyek Rekaman Aktif yang sudah diambil dari database dan kemudian panggil metode save(). +Untuk memutakhirkan rekaman dalam database, cukup ubah satu atau lebih properti obyek Rekaman Aktif yang sudah diambil dari database dan kemudian panggil metode save(). $user = UserRecord::finder()->findByName('admin'); @@ -392,7 +392,7 @@ Kapan saja Anda memanggil metode save() pada obyek TActiveRecord, obyek

    Menghapus rekaman yang sudah ada

    Untuk menghapus rekaman yang sudah ada dan diambil, cukup panggil metode delete(). - Anda juga dapat menghapus rekaman dalam database dengan kunci primer tanpa mengambil rekaman apapun menggunakan metode deleteByPk() (dan metode yang sama deleteAllByPks()). + Anda juga dapat menghapus rekaman dalam database dengan kunci primer tanpa mengambil rekaman apapun menggunakan metode deleteByPk() (dan metode yang sama deleteAllByPks()). Sebagai contoh, untuk menghapus satu atau beberapa rekaman dengan menggunakan satu atau lebih kunci primer.

    @@ -457,8 +457,8 @@ TActiveRecord menawarkan dua event, OnCreateCommand dan OnExecuteCo

    -Event OnExecuteCommand dimunculkan ketika perintah dijalankan dan hasil dari database dikembalikan. Obyek parameter TDataGatewayResultEventParameter -dari properti Result berisi data yang dikembalikan dari database. +Event OnExecuteCommand dimunculkan ketika perintah dijalankan dan hasil dari database dikembalikan. Obyek parameter TDataGatewayResultEventParameter +dari properti Result berisi data yang dikembalikan dari database. Data yang dikembalikan dapat diubah dengan setelan properti Result.

    @@ -491,7 +491,7 @@ function logger($sender,$param) } TActiveRecord::finder('MyRecord')->OnExecuteCommand[] = 'logger'; $obj->OnExecuteCommand[] = array($logger, 'log'); //setiap PHP callback yg benar. -
    +

    Hubungan Rekaman Aktif

    @@ -507,7 +507,7 @@ Dalam bagian berikut kita akan menganggap hubungan tabel antara class="figure" /> -

    Tujuannya adalah untuk mendapatkan model obyek yang mewakili ke beberapa derajat hubungan entitas dalam gambar di atas. +

    Tujuannya adalah untuk mendapatkan model obyek yang mewakili ke beberapa derajat hubungan entitas dalam gambar di atas.

    class="figure" /> @@ -534,7 +534,7 @@ CREATE TABLE bar

    Pemetaan Kunci Asing

    -

    Hubungan entitas antara tabel Teams dan Players adalah apa yang dikenal sebagai hubungan 1-M. Yaitu, satu Tim dapat berisi 0 atau lebih Pemain. Dalam batasan hubungan obyek, kita katakan bahwa obyek TeamRecord memiliki banyak obyek PlayerRecord. +

    Hubungan entitas antara tabel Teams dan Players adalah apa yang dikenal sebagai hubungan 1-M. Yaitu, satu Tim dapat berisi 0 atau lebih Pemain. Dalam batasan hubungan obyek, kita katakan bahwa obyek TeamRecord memiliki banyak obyek PlayerRecord. (Perhatikan kebalikan dari arah hubungan antara tabel dan obyek.)

    @@ -548,7 +548,7 @@ class TeamRecord extends TActiveRecord const TABLE='Teams'; public $name; public $location; - + public $players=array(); // deklarasi ini tidak diperlukan lagi sejak v3.1.2 //mendefinisikan anggota $player yang memiliki hubungan banyak dengan PlayerRecord @@ -616,7 +616,7 @@ Metode with_xxx() (di mana xxx adalah nama properti hubungan,

    Catatan: Penting untuk dimengerti bahwa obyek terkait diambil menggunakan query tambahan. Query pertama mengambil obyek sumber, misalnya TeamRecord dalam contoh kode di atas. -Query kedua dipakai untuk mengambil obyek PlayerRecord terkait. +Query kedua dipakai untuk mengambil obyek PlayerRecord terkait. Penggunaan dua query mirip dengan query tunggal menggunakan Left-Outer join dengan eksepsi bahwa hasil null pada tabel kanan tidak dikembalikan. Konsekuensi pemakaian dua tau lebih query adalah kondisi agregat dan join tidak layak menggunakan Rekaman Aktif. Untuk query di luar lingkup Rekaman Aktif, Pemeta Data SqlMap diapat dupertimbangkan.
    @@ -634,9 +634,9 @@ Kode berikut mendefinisikan kelas PlayerRecord lengkap dengan 3 hubunga class PlayerRecord extends TActiveRecord { const TABLE='Players'; - public $player_id; - public $age; - public $team_name; + public $player_id; + public $age; + public $team_name; public $team; // deklarasi ini tidak diperlukan lagi sejak v3.1.2 public $skills=array(); // deklarasi ini tidak diperlukan lagi sejak v3.1.2 @@ -656,9 +656,9 @@ class PlayerRecord extends TActiveRecord }

    -Properti $RELATIONS dari PlayerRecord mendefinisikan properti $team milik TeamRecord. -Array $RELATIONS juga mendefinisikan dua hubungan lainnya yang nanti akan kita uji dalam seksi di bawah ini. -Dalam array(self::BELONGS_TO, 'TeamRecord'), elemen pertama mendefinisikan tipe hubungan, dalam hal ini self::BELONGS_TO dan +Properti $RELATIONS dari PlayerRecord mendefinisikan properti $team milik TeamRecord. +Array $RELATIONS juga mendefinisikan dua hubungan lainnya yang nanti akan kita uji dalam seksi di bawah ini. +Dalam array(self::BELONGS_TO, 'TeamRecord'), elemen pertama mendefinisikan tipe hubungan, dalam hal ini self::BELONGS_TO dan elemen kedua adalah string 'TeamRecord' yang terkait ke nama kelas dari kelas TeamRecord. Obyek pemain dengan obyek tim terkait dapat diambil serperti berikut.

    @@ -703,7 +703,7 @@ class ProfileRecord extends TActiveRecord

    Intinya, ada hubungan "belongs to" untuk obyek yang mengaitkan entitas yang memmpunyai kolom yakni kunci asing. Dalam keadaan tertentu, kita melihat bahwa tabel Profiles mempunyai batasan kunci asing pada kolom player_id yang terkait ke tabel Players kolom player_id. Selanjutnya, obyek ProfileRecord memiliki properti ($player) yang adalah milik obyek PlayerRecord. -Demikian juga, tabel Players mempunyai batasan kunci asing pada kolom team_name yang terkait ke tabel Teams kolom name. +Demikian juga, tabel Players mempunyai batasan kunci asing pada kolom team_name yang terkait ke tabel Teams kolom name. Kemudian, obyek PlayerRecord mempunyai properti ($team) yang adalah milik obyek TeamRecord.

    @@ -756,13 +756,13 @@ dua elemen tambahan ini mirip seperti parameter yang dikirimkan ke metode fi Obyek dapat dengan mudah menangani field multi nilai dengan menggunakan koleksi sebagai nilai field. Database relasional tidak memiliki fitur ini dan dibatasi hanya ke field nilai-tunggal. Ketika Anda memetakan asosiasi satu-ke-banyak, Anda bisa menangani ini menggunakan hubungan has many, intinya menggunakan kunci asing untuk nilai-tunggal akhir dari asosiasi. Tapi asosiasi banyak-ke-banyak tidak bisa melakukan ini karena tidak ada nilai-tunggal akhir ke kunci asing yang dipegangnya.

    -Jawabannya adalah resolusi klasik yang telah dipakai oleh orang selama dekade ini yakni: buat tabel ekstra (tabel asosiasi) untuk merekam asosiasi. +Jawabannya adalah resolusi klasik yang telah dipakai oleh orang selama dekade ini yakni: buat tabel ekstra (tabel asosiasi) untuk merekam asosiasi. Ide dasarnya adalah menggunakan tabel asosiasi untuk menyimpan asosiasi. Tabel ini memiliki ID kunci asing untuk dua tabel yang dikaitkan bersama, masing-masing memiliki pasangan dari obyek yang diasosiasikan.

    Tabel asosiasi tidak mempunyai kaitan obyek dalam-memori dan kunci primernya adalah gabungan dari dua kunci primer dari tabel yang diasosiasikan. -Dalam batasan yang sederhana, tuntuk mengambil data dari tabel asosiasi, Anda melakukan dua query (secara umum, ini juga bisa dicapai menggunakan satu query yang terdiri dari join). -Anggap pengambilan koleksi SkillRecord untuk daftar obyek PlayerRecord. +Dalam batasan yang sederhana, tuntuk mengambil data dari tabel asosiasi, Anda melakukan dua query (secara umum, ini juga bisa dicapai menggunakan satu query yang terdiri dari join). +Anggap pengambilan koleksi SkillRecord untuk daftar obyek PlayerRecord. Dalam hal ini, Anda melakukan query dalam dua tahap. Tahap pertama meng-query tabel Players untuk mencari seluruh baris dari pemain yang Anda inginkan. Tahap kedua mencari obyek SkillRecord ID pemain terkait untuk setiap barisnya dalam tabel asosiasi Player_Skills menggunakan sebuah inner join.

    @@ -794,7 +794,7 @@ class SkillRecord extends TActiveRecord

    Properti statis $RELATIONS dari SkillRecord mendefinisikan bahwa properti $players memiliki banyak PlayerRecords melalui tabel asosiasi 'Player_Skills'. Dalam array(self::MANY_TO_MANY, 'PlayerRecord', 'Player_Skills'), elemen pertama mendefinisikan tipe hubungan, dalam hal ini self::HAS_MANY, -elemen kedua adalah string 'PlayerRecord' yang terkait ke nama kelas dari kelas PlayerRecord, dan elemen ketiga adalah nama dari nama tabel asosiasi. +elemen kedua adalah string 'PlayerRecord' yang terkait ke nama kelas dari kelas PlayerRecord, dan elemen ketiga adalah nama dari nama tabel asosiasi.

    Catatan: @@ -815,22 +815,22 @@ Metode with_xxx() (di mana xxx adalah nama properti hubungan,

    Untuk tabel asosiasi yang mererefensi dirinya sendiri, yaitu titik asosiasi ke tabel yang sama. Sebagai contoh, anggap tabel items dengan item terkait M-N melalui tabel asosiasi related_items. Sintaks dalam contoh berikut adalah benar untuk database PostgreSQL. Untuk database lain, lihat dokumentasinya masing-masing untuk mendefinisikan batasan kunci asing. -CREATE TABLE items +CREATE TABLE items ( - "item_id" SERIAL, + "item_id" SERIAL, "name" VARCHAR(128) NOT NULL, PRIMARY KEY("item_id") ); -CREATE TABLE "related_items" +CREATE TABLE "related_items" ( - "item_id" INTEGER NOT NULL, - "related_item_id" INTEGER NOT NULL, - CONSTRAINT "related_items_pkey" PRIMARY KEY("item_id", "related_item_id"), + "item_id" INTEGER NOT NULL, + "related_item_id" INTEGER NOT NULL, + CONSTRAINT "related_items_pkey" PRIMARY KEY("item_id", "related_item_id"), CONSTRAINT "related_items_item_id_fkey" FOREIGN KEY ("item_id") REFERENCES "items"("item_id") ON DELETE CASCADE ON UPDATE NO ACTION - NOT DEFERRABLE, + NOT DEFERRABLE, CONSTRAINT "related_items_related_item_id_fkey" FOREIGN KEY ("related_item_id") REFERENCES "items"("item_id") ON DELETE CASCADE @@ -839,7 +839,7 @@ CREATE TABLE "related_items" ); -

    Nama tabel asosiasi dalam elemen ketiga dari array hubungan dapat berisi nama kolom tabel asing. Kolom yang didefinisikan dalam tabel asosiasi harus juga didefinisikan dalam kelas rekaman (contohnya properti $related_item_id terkait ke kolom related_item_id dalam tabel related_items). +

    Nama tabel asosiasi dalam elemen ketiga dari array hubungan dapat berisi nama kolom tabel asing. Kolom yang didefinisikan dalam tabel asosiasi harus juga didefinisikan dalam kelas rekaman (contohnya properti $related_item_id terkait ke kolom related_item_id dalam tabel related_items).

    class Item extends TActiveRecord @@ -849,18 +849,18 @@ class Item extends TActiveRecord public $details; //id item asing tambahan didefinisikan dalam tabel asosiasi - public $related_item_id; + public $related_item_id; public $related_items=array(); // deklarasi ini tidak diperlukan lagi sejak v3.1.2 public static $RELATIONS=array ( - 'related_items' => array(self::MANY_TO_MANY, + 'related_items' => array(self::MANY_TO_MANY, 'Item', 'related_items.related_item_id'), ); }
    Tip: -Kunci gabungan dalam tabel asing dapat ditetapkan sebagai nilai dipisahkan koma diantara kurung buka/tutup. Contohnya 'related_items.(id1,id2)'. +Kunci gabungan dalam tabel asing dapat ditetapkan sebagai nilai dipisahkan koma diantara kurung buka/tutup. Contohnya 'related_items.(id1,id2)'.