<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://wiki.x2crm.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jake</id>
		<title>X2Engine - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="http://wiki.x2crm.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jake"/>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/wiki/Special:Contributions/Jake"/>
		<updated>2026-05-20T09:10:14Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.28.0</generator>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1943</id>
		<title>REST API Reference</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=REST_API_Reference&amp;diff=1943"/>
				<updated>2015-10-29T21:16:33Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* Access Credentials */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
X2Engine's second-generation HTTP-based API, '''available as of version 4.1''', is (for the most part) REST-ful, and includes many improvements over the original API. &lt;br /&gt;
&lt;br /&gt;
The source code of the main components used in this API are:&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/controllers/Api2Controller.php protected/controllers/Api2Controller.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/ResponseBehavior.php]&lt;br /&gt;
* [https://github.com/X2Engine/X2Engine/blob/master/x2engine/protected/components/util/ResponseUtil.php protected/components/util/ResponseUtil.php]&lt;br /&gt;
Functional tests for the API can be found alongside the tests for the legacy API, in [https://github.com/X2Engine/X2Engine/tree/master/x2engine/protected/tests/api protected/tests/api].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
This API within X2Engine, can be accessed via the URI&lt;br /&gt;
  index.php/api2&lt;br /&gt;
It also:&lt;br /&gt;
* Exclusively uses JSON for data input and output&lt;br /&gt;
* Tends to use similar URIs for both input and output (distinguishing operations via the request method) &lt;br /&gt;
* Uses a variety of server response codes to distinguish error scenarios in the case of an unsuccessful transaction&lt;br /&gt;
* Uses the &amp;quot;HTTP Basic Auth&amp;quot; method for authentication&lt;br /&gt;
&lt;br /&gt;
For example, to create a contact, one would send a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request with its body a JSON-encoded attributes list to the URI &lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
with the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;, and the request body as (for example):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;firstName&amp;quot;:&amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;:&amp;quot;Smith&amp;quot;,&amp;quot;visibility&amp;quot;:1,&amp;quot;email&amp;quot;:&amp;quot;johnsmith@example.com&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If creation of the contact is successful, the server should respond with status code &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt; (&amp;quot;Created&amp;quot;), and the response should contain a &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header with the full URL (including protocol) of the newly created contact (in addition to all the attributes of the new contact). If for example the new contact's ID is 123, that URI would be &lt;br /&gt;
  index.php/api2/Contacts/123.json&lt;br /&gt;
and a GET request to that URI would elicit a response from the server whose body contains a JSON-encoded list of attributes.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats, Terminology and Conventions ==&lt;br /&gt;
Note, the above example, the URI to which the &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request is sent (to create the contact) is referred to in this documentation as a '''base &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Base URIs, when requested using the &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; method, return a JSON-encoded array, each entry in the array corresponding to a record and being a dictionary of column:value pairs. So for example, if sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the contacts model base URI&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
the response would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[...,{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...},...]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Whenever a URI points to an object or resource uniquely identified with a specific database record, that URI is referred to as a '''direct &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt;'''. Direct URIs end in &amp;quot;.json&amp;quot; and respond to &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests with JSON-encoded dictionary objects of column values for the record as it is in the database. So, the URI in the above example (&amp;lt;tt&amp;gt;index.php/api2/Contacts/123.json&amp;lt;/tt&amp;gt;) would respond with:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;{&amp;quot;email&amp;quot;: &amp;quot;johnsmith@example.com&amp;quot;,&amp;quot;firstName&amp;quot;: &amp;quot;John&amp;quot;,&amp;quot;lastName&amp;quot;: &amp;quot;Smith&amp;quot;,...}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direct URIs will almost always end in &amp;quot;.json&amp;quot;, and base URIs will not. '''In general, the following convention applies''' almost universally within the API: ''If the &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; ends in &amp;quot;.json&amp;quot;, the resource will be a JSON-encoded dictionary object. Otherwise, it will be a JSON-encoded array. In the latter case, if each element of the array is a dictionary object, the dictionary objects should have uniform structure.''&lt;br /&gt;
&lt;br /&gt;
== Access Credentials ==&lt;br /&gt;
To use the API, you will need to obtain X2Engine API credentials, which include a username and an API key. An X2Engine user with administrative privileges can get or set API keys in the Users module, by going to the edit page for that user. To summarize:&lt;br /&gt;
# As the administrator, go to the user management module. You'll find it under &amp;quot;Users&amp;quot;&lt;br /&gt;
# Click on the desired user&lt;br /&gt;
# Click &amp;quot;Update User&amp;quot;&lt;br /&gt;
# See the &amp;quot;API Key&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
While there is a user in this section called &amp;quot;API User&amp;quot; you should not use it for this version of the API. That user exists to maintain backwards compatibility for a very old version of the API so that users who have set up API connectors with those endpoints will not have their code break. This user will not function with the current API and attempting to use it will generate a 403 error.&lt;br /&gt;
&lt;br /&gt;
== Explore the API Using Your Web Browser ==&lt;br /&gt;
Once you have API credentials, you can examine the web API using your web browser by making &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to locations within &amp;lt;tt&amp;gt;index.php/api2&amp;lt;/tt&amp;gt; (simply by typing them into your browser's location bar). You can get a nicer view of the data returned by the server by installing a JSON viewing plugin in your web browser. A recommended plugin for this purpose is [http://jsonview.com/ JSONView], which is available [https://addons.mozilla.org/en-us/firefox/addon/jsonview/ for Firefox] and [https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc for Google Chrome] (it's an unofficial port, but the developer of the API and author of this article uses it).&lt;br /&gt;
&lt;br /&gt;
=== Example 1: hello, world ===&lt;br /&gt;
Try visiting the following URI within X2Engine:&lt;br /&gt;
  index.php/api2/appInfo.json&lt;br /&gt;
Initially you will be prompted to enter the username and password to complete authentication; enter the API key corresponding to the user in the password field. The above URL should respond with a JSON string containing some basic info about the X2Engine application.&lt;br /&gt;
&lt;br /&gt;
=== Example 2: direct access ===&lt;br /&gt;
You can get the ID of any given account record by going to it inside X2Engine as you normally would and examining the URI. It should generally look something like this:&lt;br /&gt;
  index.php/accounts/32&lt;br /&gt;
or:&lt;br /&gt;
  index.php/accounts/id/32&lt;br /&gt;
In both of the above examples, the primary key value (id) of the record in question is 32. '''To view it in the API:'''&lt;br /&gt;
  index.php/api2/Accounts/32.json&lt;br /&gt;
&lt;br /&gt;
Note how in the API, the first letter of &amp;quot;Accounts&amp;quot; in the direct URI is capitalized. This is because active record data in the API is accessed not via specifying the module containing the active record class, or to which the class corresponds, but by specifying the actual ''class name'' to use when accessing (or querying) data.&lt;br /&gt;
&lt;br /&gt;
=== Example 3: querying ===&lt;br /&gt;
  index.php/api2/Actions?_order=-id&amp;amp;_limit=3&lt;br /&gt;
This will show you the last 3 action records (i.e. emails, call logs, to-do's) created in the system (which the current acting API user has permission to view), in descending order of their primary key values (column &amp;quot;id&amp;quot;). If you have no action records in your system, you should receive an empty array.&lt;br /&gt;
&lt;br /&gt;
== Prerequisites for API Applications ==&lt;br /&gt;
When writing an application to interface with X2Engine via the API, it is required or strongly recommended that your language/coding environment of choice:&lt;br /&gt;
&lt;br /&gt;
* Have a JSON parsing and encoding library available&lt;br /&gt;
* Have a library for making HTTP requests which can:&lt;br /&gt;
** Set request headers&lt;br /&gt;
** Parse response headers and read the response status code&lt;br /&gt;
** Natively support requests using HTTP Basic Auth for access&lt;br /&gt;
** Read responses even when the response code is not in the &amp;quot;success&amp;quot; category (2xx)&lt;br /&gt;
** Make &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests&lt;br /&gt;
* Can access the network&lt;br /&gt;
&lt;br /&gt;
Many high-level languages, such as Perl, PHP, Python and Ruby, meet these requirements. The specific usage of these languages is beyond the scope of this article; you will need to refer to the documentation of the library/libraries in use.&lt;br /&gt;
&lt;br /&gt;
It is expected in the near future that a growing number of official API access classes (each in a different programming language) will be available for quick and easy development of API applications.&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
As stated before, the API uses &amp;quot;HTTP Basic&amp;quot; authorization. Many HTTP client libraries will have native methods of setting headers for HTTP basic auth. It is recommended that you use such a method for authentication and read the relevant documentation, rather than setting headers manually, as that will save time and more likely lead to quicker success.&lt;br /&gt;
&lt;br /&gt;
In all other cases, to authenticate and access the API using this method, each request must include the &amp;lt;tt&amp;gt;Authorization&amp;lt;/tt&amp;gt; header. To compose the header, first combine the username and API key into a single string with a colon, as:&lt;br /&gt;
  {username}:{userKey}&lt;br /&gt;
Next, obtain the string in Base-64 encoding. For example, '''&amp;lt;tt&amp;gt;username:password&amp;lt;/tt&amp;gt;''' in base 64 is &amp;lt;tt&amp;gt;dXNlcjpwYXNzd29yZA==&amp;lt;/tt&amp;gt;. Thus, the resulting header would look like this:&lt;br /&gt;
  Authorization: Basic dXNlcjpwYXNzd29yZA==&lt;br /&gt;
&lt;br /&gt;
See also the [[wikipedia:Basic access authentication#Client side|the Wikipedia article on this topic]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Caveats ===&lt;br /&gt;
'''Note, firstly:''' because headers are sent without any built-in encryption, it is highly recommended that you use the API over HTTPS (HTTP encrypted using TLS), if available, or make API requests only within a network where packets are not easily intercepted.&lt;br /&gt;
&lt;br /&gt;
Furthermore, if you have password-protected any web directories that contain X2Engine, you will need to make a &amp;quot;Satisfy any&amp;quot; exception for URIs within the API, or clients may not be able to authenticate.&lt;br /&gt;
&lt;br /&gt;
= Model-based Input and Output =&lt;br /&gt;
Most of the modules in X2Engine (i.e. Contacts) will each have a corresponding active record model. This model is what is customized whenever adding a custom field. It is essentially a PHP class that is a child of [[x2doc:X2Model|X2Model]] (see: [[X2Model and Dynamic Fields]] for more information). Almost all API-based functions involving such data objects will contain the name of that class in the URL, i.e.&lt;br /&gt;
  index.php/api2/Accounts&lt;br /&gt;
  index.php/api2/Contacts/135/Actions&lt;br /&gt;
  index.php/api2/Contacts/112.json&lt;br /&gt;
In general, the base URI for functions pertaining to models is&lt;br /&gt;
  index.php/api2/{_class} &lt;br /&gt;
where &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; is the class. The direct URI is generally:&lt;br /&gt;
  index.php/api2/{_class}/{_id}.json&lt;br /&gt;
where &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt; is the ID of the record to access.&lt;br /&gt;
&lt;br /&gt;
== Getting Model Classes ==&lt;br /&gt;
From the following URI one can obtain a list of models:&lt;br /&gt;
  index.php/api2/models&lt;br /&gt;
Each model in the list is a dictionary containing:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;modelName&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The class of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;title&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The human-readable name of the model&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |&amp;lt;tt&amp;gt;attributes&amp;lt;/tt&amp;gt;&lt;br /&gt;
|An array of attribute names&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
To include only fully-supported classes versus partially-supported model classes, include the &amp;quot;partialSupport&amp;quot; parameter and have it equal zero:&lt;br /&gt;
  index.php/api2/models?partialSupport=0&lt;br /&gt;
&lt;br /&gt;
== Fully-Supported Model Classes ==&lt;br /&gt;
As of this writing, X2Engine by default has the following model classes that are fully supported in the API &amp;amp;mdash; meaning, all or nearly all of the most essential functionality that is possible in X2Engine via a web browser, in terms of manipulation of persistent data storage, is also possible via the API.&lt;br /&gt;
* &amp;lt;tt&amp;gt;Accounts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;BugReports&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Contacts&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Campaign&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Opportunity&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Product&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;Services&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Additionally, any custom modules will also have corresponding active record models fully supported by the API. This should usually be the same name as the module, but without spaces and the first letter always capitalized. If in doubt, to find the model class corresponding to a given module (i.e. a custom module), look in the &amp;quot;models&amp;quot; sub-directory of that module. The name of the file excluding the extension (&amp;lt;tt&amp;gt;.php&amp;lt;/tt&amp;gt;) should be the name of the class. For instance, in the file &amp;lt;tt&amp;gt;protected/modules/contacts/models/Contacts.php&amp;lt;/tt&amp;gt; there should be the following line:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class Contacts extends X2Model {&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Partially-Supported Models ==&lt;br /&gt;
Manipulation of data using the following models (or certain aspects of the following models) is not fully supported in the API as of this writing &amp;amp;mdash; meaning, while most operations are possible, some important functionality is not yet possible:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Actions can be created, updated, viewed, queried and deleted as all other model types. However:&lt;br /&gt;
* Action completion and backdating (setting or overwriting the completion date) is not yet supported unless one enables &amp;quot;raw input&amp;quot; (Platinum Edition only), because &amp;lt;tt&amp;gt;completeDate&amp;lt;/tt&amp;gt; is a &amp;quot;read-only&amp;quot; field&lt;br /&gt;
* Associated &amp;quot;action timer&amp;quot; records (applicable only to Professional/Platinum edition) cannot be accessed or manipulated&lt;br /&gt;
* Comparisons based on action description in queries:&lt;br /&gt;
** They are unaffected by options that control comparisons; the comparison is un-escaped &amp;quot;LIKE&amp;quot; and the criteria combination operator is effectively &amp;quot;AND&amp;quot; (&amp;lt;tt&amp;gt;_partial=1&amp;amp;_escape=0&amp;lt;/tt&amp;gt;). &lt;br /&gt;
** They will also be very slow; effectively, the comparison in the query is being performed on a &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt; column in a joined table&lt;br /&gt;
&lt;br /&gt;
The limitations of filtering by action description are endemic to how the &amp;quot;field&amp;quot; is actually stored in a different database table than the contacts, and the type of the column is &amp;lt;tt&amp;gt;TEXT&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Docs&amp;lt;/tt&amp;gt; &lt;br /&gt;
|Can be accessed/manipulated as with other models. However, &amp;quot;edit permissions&amp;quot; do not work the same way as they do in the application.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Groups&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Groups can be queried, viewed and created, albeit only by an administrative user, and in a very limited capacity. Users cannot be added to or removed from groups; manipulating the associated &amp;quot;group-to-user&amp;quot; data is not yet possible.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Media&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Media records can be accessed and manipulated as with any other model, but the API does not yet support directly uploading files to go with them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;Quote&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Quote records can be accessed and manipulated, but associated &amp;quot;line items&amp;quot; data cannot be viewed or manipulated.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2Leads&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type can be fully accessed and manipulated, but there is not yet any built-in action available for directly converting a lead to an opportunity.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;X2List&amp;lt;/tt&amp;gt;&lt;br /&gt;
|This data type (contact lists in X2Engine) can be accessed and manipulated, but the actual contents of the list (whether dynamic or static) cannot.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating, Viewing Updating and Deleting Records ==&lt;br /&gt;
To create a record, perform a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to the base URI for the model, i.e.&lt;br /&gt;
  index.php/api2/Contacts&lt;br /&gt;
to create a contact. As mentioned in the example in the [[#Introduction]], the body of the request must be a JSON-encoded library of attributes to set in the model, and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header must be set to &amp;lt;tt&amp;gt;application/json&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To view, update or delete a record, first determine its direct URI within the API, as set in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header if creation was successful, or as determined via its class and id, i.e.&lt;br /&gt;
  index.php/api2/Contacts/33.json&lt;br /&gt;
to specify a contact record with its id equal to 33.&lt;br /&gt;
&lt;br /&gt;
To update a record, send a &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt; request to a direct URI, and set the body and &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header as one would in a &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; request to create such a record. Finally, to delete the record, send a DELETE request to that same direct URI. It is not necessary in the case of deletion to include a body or set the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header.&lt;br /&gt;
&lt;br /&gt;
== Direct Manipulation by Attributes ==&lt;br /&gt;
Given a set of uniquely-identifying attribute names and values, it is possible to directly access and manipulate an existing X2Model-based record by without first querying it. The direct URI for this use case is:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/{_class}/by:{_findBy}.json&lt;br /&gt;
&lt;br /&gt;
where the query parameter &amp;lt;tt&amp;gt;{_findBy}&amp;lt;/tt&amp;gt; is a list of key and value pairs formatted as &amp;lt;tt&amp;gt;key1=value1;key2=value2&amp;lt;/tt&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
For example: provided the email address &amp;quot;james@example.com&amp;quot; and first name &amp;quot;James&amp;quot; uniquely identify the contact with ID of 457, the following two direct access URIs are equivalent:&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json&lt;br /&gt;
  index.php/api2/Contacts/457.json&lt;br /&gt;
&lt;br /&gt;
Furthermore, note what happens in the following scenarios:&lt;br /&gt;
&lt;br /&gt;
'''If the set of attributes (i.e. email address) is not unique to a single record:''' the find-by-attributes direct URI will return with a 300 status and an [[#Error Objects|error object]] containing the two special properties: ''queryUri'', the URI to query for records by the attributes given, and: ''directUris'', a list containing the direct URI of each matching record.&lt;br /&gt;
&lt;br /&gt;
''' If no contact matches the attributes given:''' the URI will respond with a 404 status.&lt;br /&gt;
&lt;br /&gt;
=== Using the first matching record ===&lt;br /&gt;
If one wants to forcefully select and use the first record matching the attributes, regardless of how many match, append the &amp;lt;tt&amp;gt;_useFirst&amp;lt;/tt&amp;gt; parameter and set equal to 1, i.e.&lt;br /&gt;
&lt;br /&gt;
  index.php/api2/Contacts/by:email=james@example.com;firstName=James.json?_useFirst=1&lt;br /&gt;
&lt;br /&gt;
== Working With Associated Actions ==&lt;br /&gt;
The Actions model is unique in that it can be &amp;quot;associated&amp;quot; with almost any other type of model record. Actions records comprise all &amp;quot;history&amp;quot; items on any given record, i.e. emails, calls logged, notes, calendar events and also plain actions. Actions that have an association with another model can be used via clean URIs that point to &amp;quot;within&amp;quot; the associated model.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
&lt;br /&gt;
To view/query all actions associated with an active record model of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;, use the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
&lt;br /&gt;
For instance, one could find all actions, including emails, on contact id=1233, via:&lt;br /&gt;
  index.php/api2/Contacts/1233/Actions&lt;br /&gt;
&lt;br /&gt;
To view an individual action of id &amp;lt;tt&amp;gt;{_actionId}&amp;lt;/tt&amp;gt;, one can use this direct URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&lt;br /&gt;
=== Creating, Updating and Deleting Actions ===&lt;br /&gt;
Similar to the the basic model access API function, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be sent to associated action URIs to create/modify/delete records just as those URIs can also be used to view data. &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions&lt;br /&gt;
to create a new action associated with model record of class &amp;lt;tt&amp;gt;{_class}&amp;lt;/tt&amp;gt; and id &amp;lt;tt&amp;gt;{_id}&amp;lt;/tt&amp;gt;. Then, using the direct URI,&lt;br /&gt;
  index.php/api2/{_class}/{_id}/Actions/{_actionId}.json&lt;br /&gt;
&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; requests can be used to modify/delete an existing associated action record.&lt;br /&gt;
&lt;br /&gt;
= Metadata Functions =&lt;br /&gt;
There is &amp;quot;structural&amp;quot; metadata that one can retrieve through the API to more effectively determine how to proceed with future API transactions. There are also some functions in the API that pertain to functionality associated with model records, but do not modify data within the records themselves, and create associated metadata.&lt;br /&gt;
&lt;br /&gt;
== Fields ==&lt;br /&gt;
One can access field metadata for a given model class by using the following base URI (which supports querying):&lt;br /&gt;
  index.php/api2/{_class}/fields&lt;br /&gt;
One can directly access the metadata of a field by its name via the following direct URI format:&lt;br /&gt;
  index.php/api2/{_class}/fields/{_fieldName}.json&lt;br /&gt;
For instance, this URI&lt;br /&gt;
  index.php/api2/Contacts/fields/leadSource.json&lt;br /&gt;
would respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
{&amp;quot;id&amp;quot;:&amp;quot;88&amp;quot;, &amp;quot;modelName&amp;quot;:&amp;quot;Contacts&amp;quot;, &amp;quot;fieldName&amp;quot;:&amp;quot;leadSource&amp;quot;, &amp;quot;attributeLabel&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;modified&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;custom&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;dropdown&amp;quot;, &amp;quot;required&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;uniqueConstraint&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;safe&amp;quot;:&amp;quot;1&amp;quot;, &amp;quot;readOnly&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;linkType&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;searchable&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;relevance&amp;quot;:&amp;quot;&amp;quot;, &amp;quot;isVirtual&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;defaultValue&amp;quot;:null,&amp;quot;keyType&amp;quot;:null}&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Field-Level Permissions ==&lt;br /&gt;
Any given user's access level to a field can be controlled by assigning them to a role and then setting field permissions for that role via ''Manage Roles'' under ''Admin''.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following will respond with a dictionary of active record model attributes, each value corresponding to field-level access permissions for that field granted the current acting API user.&lt;br /&gt;
  index.php/api2/{_class}/fieldPermissions.json&lt;br /&gt;
For example, as the default administrator, for model Contacts, getting the following:&lt;br /&gt;
  index.php/api2/Accounts/fieldPermissions.json&lt;br /&gt;
&lt;br /&gt;
will respond with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;leadtype&amp;quot;:2, &amp;quot;leadSource&amp;quot;:2, &amp;quot;leadstatus&amp;quot;:2, &amp;quot;leadDate&amp;quot;:2, &amp;quot;leadscore&amp;quot;:2, &amp;quot;interest&amp;quot;:2, &amp;quot;dealvalue&amp;quot;:2, &amp;quot;closedate&amp;quot;:2, &amp;quot;rating&amp;quot;:2, &amp;quot;dealstatus&amp;quot;:2, &amp;quot;name&amp;quot;:2, &amp;quot;nameId&amp;quot;:1, &amp;quot;id&amp;quot;:1, &amp;quot;website&amp;quot;:2, &amp;quot;type&amp;quot;:2, &amp;quot;visibility&amp;quot;:2, &amp;quot;annualRevenue&amp;quot;:2, &amp;quot;phone&amp;quot;:2, &amp;quot;tickerSymbol&amp;quot;:2, &amp;quot;address&amp;quot;:2, &amp;quot;city&amp;quot;:2, &amp;quot;state&amp;quot;:2, &amp;quot;country&amp;quot;:2, &amp;quot;zipcode&amp;quot;:2, &amp;quot;parentAccount&amp;quot;:2, &amp;quot;primaryContact&amp;quot;:2, &amp;quot;employees&amp;quot;:2, &amp;quot;assignedTo&amp;quot;:2, &amp;quot;createDate&amp;quot;:1, &amp;quot;description&amp;quot;:2, &amp;quot;lastUpdated&amp;quot;:1, &amp;quot;lastActivity&amp;quot;:1, &amp;quot;updatedBy&amp;quot;:1}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For each of these entries, the number associated with the field indicates the following:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |0&lt;br /&gt;
|No access; when directly accessing a model record, the response data will not include the content of that field&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |1&lt;br /&gt;
|Read-only access; responses will include the content of that field, but any input to that field will be discarded&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; |2&lt;br /&gt;
|Read/write access. The current API user can both view and edit data in the field.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zapier-Friendly Fields ==&lt;br /&gt;
There is similarly a dynamic fields API action that returns an array of fields intended for use by Zapier. The object format returned from this action is described in [https://zapier.com/developer/documentation/reference/#action-fields-custom Action Fields (Custom)] in the Zapier developer documentation.&lt;br /&gt;
&lt;br /&gt;
The base URI is:&lt;br /&gt;
  index.php/api2/{_class}/zapierFields&lt;br /&gt;
&lt;br /&gt;
This action is generally useful for API usage insofar as it will also return ranges of acceptable values for each field, if applicable, in the '''&amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt;''' property. For example, if the type of a field is &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, the dropdown options will be returned in the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of each element in the returned array. Similarly, if the type of the field is &amp;lt;tt&amp;gt;assignment&amp;lt;/tt&amp;gt;, the element's &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property will include a list of users and groups. Furthermore, it has the ability to easily select only fields of a given permission level or greater. For this, use the &amp;lt;tt&amp;gt;_permissionLevel&amp;lt;/tt&amp;gt; parameter. For example, to get all writable fields in Contacts:&lt;br /&gt;
  index.php/api2/Contacts/zapierFields?_permissionLevel=2&lt;br /&gt;
(see [[#Field-Level Permissions]] for more information)&lt;br /&gt;
&lt;br /&gt;
Unfortunately, the action does not support querying, although it does not need to for its intended purpose.&lt;br /&gt;
&lt;br /&gt;
== Dropdowns ==&lt;br /&gt;
Note, to get a list of acceptable values for a given model in cases when the field type is not &amp;lt;tt&amp;gt;dropdown&amp;lt;/tt&amp;gt;, use the &amp;lt;tt&amp;gt;choices&amp;lt;/tt&amp;gt; property of data returned by [[#Zapier-Friendly Fields]].&lt;br /&gt;
&lt;br /&gt;
Some fields, i.e. lead source in contacts, are of type &amp;quot;dropdown&amp;quot;; their content is intended to be either blank, or an option in a static list. Dropdown menus can be queried at base URI&lt;br /&gt;
  index.php/api2/dropdowns&lt;br /&gt;
Dropdown fields can also be directly accessed via&lt;br /&gt;
  index.php/api2/dropdowns/{_id}.json&lt;br /&gt;
To find out if a field is of type dropdown, and which dropdown menu it uses: the value for &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt; in the field's metadata record should be &amp;quot;dropdown&amp;quot;, and the &amp;lt;tt&amp;gt;linkType&amp;lt;/tt&amp;gt; field should contain the dropdown's ID. So, using the example in [[#Fields]], the corresponding dropdown record is at&lt;br /&gt;
  index.php/api2/dropdowns/103.json&lt;br /&gt;
which contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;{&amp;quot;id&amp;quot;:&amp;quot;103&amp;quot;, &amp;quot;name&amp;quot;:&amp;quot;Lead Source&amp;quot;, &amp;quot;options&amp;quot;:{&amp;quot;None&amp;quot;:&amp;quot;None&amp;quot;, &amp;quot;Google&amp;quot;:&amp;quot;Google&amp;quot;, &amp;quot;Facebook&amp;quot;:&amp;quot;Facebook&amp;quot;, &amp;quot;Walk In&amp;quot;:&amp;quot;Walk In&amp;quot;}, &amp;quot;multi&amp;quot;:&amp;quot;0&amp;quot;, &amp;quot;parent&amp;quot;:null, &amp;quot;parentVal&amp;quot;:null}&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, for convenience's sake the &amp;quot;options&amp;quot; field won't be returned verbatim as the raw JSON (that's how the options are stored). Rather, that field will be decoded into a sub-dictionary of the overall object.&lt;br /&gt;
&lt;br /&gt;
== Relationships ==&lt;br /&gt;
It is possible to create, view, and delete relationships between supported API models in X2Engine via the API.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Relations functionality is in general accessed within the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/relationships&lt;br /&gt;
&lt;br /&gt;
So, to view all relationships going to or from account 131:&lt;br /&gt;
  index.php/api2/Accounts/131/relationships&lt;br /&gt;
&lt;br /&gt;
To view the contents (related model class and ID) of a specific relationship on the account (let's say the relation record has its id=304281 for instance):&lt;br /&gt;
  index.php/api2/Accounts/131/relationships/304281.json&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Relationships&amp;lt;/tt&amp;gt; active record model has the following attributes that can be used in queries:&lt;br /&gt;
;id&lt;br /&gt;
: Unique numeric identifier for the relationship&lt;br /&gt;
;&amp;lt;tt&amp;gt;firstType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;firstId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at one end of the relationship, respectively&lt;br /&gt;
;&amp;lt;tt&amp;gt;secondType&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;secondId&amp;lt;/tt&amp;gt;&lt;br /&gt;
: The model class and record ID at the other end of the relationship, respectively&lt;br /&gt;
&lt;br /&gt;
For instance, to find all outgoing relationships with Accounts to contact id=126:&lt;br /&gt;
  index.php/api2/Contacts/126/relationships?secondType=Accounts&lt;br /&gt;
&lt;br /&gt;
=== Adding/Removing Relationships ===&lt;br /&gt;
To create a new relationship, sent &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the base URI.&lt;br /&gt;
&lt;br /&gt;
To remove a relationship, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to the direct URI of the record, i.e. &amp;lt;tt&amp;gt;index.php/api2/Accounts/131/relationships/304281.json&amp;lt;/tt&amp;gt; in the earlier example.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
All basic, fully-supported models in X2Engine should support tagging. Tags cannot be individually modified, but can only be created, viewed, queried and removed, in order to enforce preservation of the important metadata such as who added the tag and at what date.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;span class=&amp;quot;noglossary&amp;quot;&amp;gt;URI&amp;lt;/span&amp;gt; Formats ===&lt;br /&gt;
Tags on a given model record can be retrieved via &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at the following base URI:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags&lt;br /&gt;
The response should be a flat array of tag names. For example, if account 51 has tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot;, sending &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; to the following URI&lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
will yield:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;[&amp;quot;#customer&amp;quot;,&amp;quot;#local&amp;quot;]&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To view more extensive metadata of the tag, i.e. who added the tag and at what date:&lt;br /&gt;
  index.php/api2/{_class}/{_id}/tags/{_tagName}.json&lt;br /&gt;
i.e.&lt;br /&gt;
  index.php/api2/Accounts/51/tags/local.json&lt;br /&gt;
The above will return a dictionary of &amp;lt;tt&amp;gt;x2_tags&amp;lt;/tt&amp;gt; column names and values. Note, the tag name in the URI must either exclude the preceding hash mark or include it via its corresponding URL encoding sequence, &amp;lt;tt&amp;gt;'''%23'''&amp;lt;/tt&amp;gt;. This is because the hash mark is a special character in the HTTP protocol and will interfere with proper resolution of the URI. Using the above example:&lt;br /&gt;
  index.php/api2/Accounts/51/tags/%23local.json&lt;br /&gt;
That URI will return the exact same data as the previous URI; they are considered equivalent.&lt;br /&gt;
&lt;br /&gt;
=== Adding Tags to a Record ===&lt;br /&gt;
Adding tags also utilizes that same URI scheme as with viewing tags. To add one or more tags, send them as string elements in a flat JSON-encoded array via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to that location. For example, to use the previous example, one can apply the tags &amp;quot;#customer&amp;quot; and &amp;quot;#local&amp;quot; from &amp;quot;record 51&amp;quot; to record 52 by &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;-ing the same JSON returned from a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; at: &lt;br /&gt;
  index.php/api2/Accounts/51/tags&lt;br /&gt;
to:&lt;br /&gt;
  index.php/api2/Accounts/52/tags&lt;br /&gt;
&lt;br /&gt;
=== Removing Tags ===&lt;br /&gt;
To delete a tag, send a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; request to the direct viewing location of the tag. So, for instance, to delete &amp;quot;#local&amp;quot; from account 51, send &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;index.php/api2/Accounts/51/tags/local.json&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Querying Data =&lt;br /&gt;
Almost any &amp;quot;base&amp;quot; URI, which can be used for accessing all records of a type or for creating new records of a type, can also be used for querying records of that type. Responses to queries (and &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; requests to these URIs in general) will always be JSON-encoded arrays of records, each record represented as an attribute dictionary.&lt;br /&gt;
&lt;br /&gt;
Options for searching, as well as attributes to match column values against (for filtering), are all specified as query parameters (a.k.a. &amp;quot;get parameters&amp;quot;). The search options, to protect against name collisions with column names, each have names that begin with an underscore, i.e. &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== General Search Option Parameters ==&lt;br /&gt;
In queries, one can use a variety of advanced options that include sorting, pagination, partial matching, and in some cases tags.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Parameter&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Default&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Usage&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_escape&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Wildcard usage&lt;br /&gt;
|&amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set 0 in parameters to allow characters like &amp;quot;%&amp;quot; and &amp;quot;_&amp;quot; to be used as SQL wildcards in search filter attributes. Controls the resultant value of the &amp;lt;tt&amp;gt;$escape&amp;lt;/tt&amp;gt; argument sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] in configuring the search. Note, to perform wildcard searches properly, the parameter &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt; must be set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; so that &amp;lt;tt&amp;gt;CDbCriteria&amp;lt;/tt&amp;gt; uses &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; for the value comparison. For info on SQL wildcards and comparisons, see: [http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html MySQL: String Comparison Functions]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_limit&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page size&lt;br /&gt;
|may vary&lt;br /&gt;
|Set to a number to control the maximum number of records to include in the results of the search. The default and maximum page size is &amp;lt;tt&amp;gt;1000&amp;lt;/tt&amp;gt;, and in  ''Platinum Edition'' this default amount is user-configurable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_or&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Use &amp;quot;OR&amp;quot; operator &lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to make the operator used for combining search criteria &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; instead of the default, &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_order&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Sorting&lt;br /&gt;
|none&lt;br /&gt;
|Set equal to the name of a column to sort by, optionally prefixed with a plus or minus sign to specify ascending or descending order (respectively). For example, &amp;lt;tt&amp;gt;_order=-leadScore&amp;lt;/tt&amp;gt; in a Contacts query sorts contacts by lead score with the highest-scored contacts first. Note, sorting applies not only to the current page but to the data set spanning all pages. Thus, if the total number of possible results is larger than the page size, the set of results shown in the current page will be affected.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_page&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Page number&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|The zero-starting-point page number of the data. Useful for when the query would return more results than the page size specified.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_partial&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Partial match&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to enable partial matching in search filters. This parameter controls the value sent to [[yii:CDbCriteria#compare-detail|CDbCriteria.compare()]] as the &amp;lt;tt&amp;gt;$partialMatch&amp;lt;/tt&amp;gt; argument. If true, the &amp;lt;tt&amp;gt;LIKE&amp;lt;/tt&amp;gt; comparison will be used; otherwise, full equality will be used as the comparison.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Inclusive tag search&lt;br /&gt;
|&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When performing tag-based searches, set to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to indicate to include records with ''any'' of the specified tags rather than all of them.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt;&lt;br /&gt;
|Has tag(s)&lt;br /&gt;
|none&lt;br /&gt;
|When querying tag-supporting [[x2doc:X2Model|X2Model]] sub-classes (meaning, those which can be tagged), this can be included and set to a comma-delineated list of tags. This will restrict results to records having all of said tags, or if &amp;lt;tt&amp;gt;_tagOr&amp;lt;/tt&amp;gt; is enabled, any of the tags. Note, tag names should not contain their preceding &amp;quot;#&amp;quot; (or, at least should not contain it without URL-encoding it) because &amp;quot;#&amp;quot; is a special character in the HTTP protocol that will interfere with how your request to the server is interpreted.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Query Parameters ==&lt;br /&gt;
Appending option parameters proceeds as it would for any script that can receive URL-encoded variables via the request: follow the base URI with a question mark, and delineate &amp;lt;tt&amp;gt;[name]=[value]&amp;lt;/tt&amp;gt; parameter declarations with ampersands ''(note: this might be different, depending on your web server's configuration, but the nearly-ubiquitous default is ampersand-delineation)''. For example:&lt;br /&gt;
  index.php/api2/Contacts?_limit=10&amp;amp;firstName=Harry&amp;amp;lastName=P%25&amp;amp;_partial=1&amp;amp;_escape=0&amp;amp;_order=+lastName&lt;br /&gt;
This will return the first ten contacts out the list of contacts having first name &amp;quot;Harry&amp;quot; and last name beginning with &amp;quot;P&amp;quot;, sorted alphabetically by last name.&lt;br /&gt;
&lt;br /&gt;
== Searching For Models By Tag ==&lt;br /&gt;
In addition to the &amp;lt;tt&amp;gt;_tags&amp;lt;/tt&amp;gt; search option, there is a &amp;quot;pretty&amp;quot; dedicated base URI format for tag searching:&lt;br /&gt;
  index.php/api2/tags/{_tags}/{_class}&lt;br /&gt;
So, for instance, to find all contacts with the tags &amp;quot;#customer&amp;quot; and &amp;quot;#important&amp;quot;:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts&lt;br /&gt;
The above is equivalent to&lt;br /&gt;
  index.php/api2/Contacts?_tags=customer,important&lt;br /&gt;
Note, additional search parameters can also be included. For instance, to return the first ten most recently updated contacts with the above tags:&lt;br /&gt;
  index.php/api2/tags/customer,important/Contacts?_order=-lastUpdated&amp;amp;_limit=10&lt;br /&gt;
&lt;br /&gt;
The reason for this is to express tags as categories, and thus in a loose sense &amp;quot;folders&amp;quot; in which one would find records.&lt;br /&gt;
&lt;br /&gt;
= Web Hooks =&lt;br /&gt;
To develop real-time integration, that is to say, to have data sent from X2Engine to a third-party service immediately when a triggering event occurs, the best method is web hooks. Note, there is also the means of sending web requests to external URLs via the &amp;quot;Remote API Call&amp;quot; X2Flow action. For more information about this feature, see [http://www.x2engine.com/x2flow_user_manual/#remoteAPI the X2Flow documentation] for this action.&lt;br /&gt;
&lt;br /&gt;
While said X2Flow action may suffice in many basic use cases, there are limitations to it that are addressed by web hooks:&lt;br /&gt;
* Configuring X2Flow cannot be performed via the API&lt;br /&gt;
* The action (making a web request) cannot be performed on a per-user basis (i.e. making a different request for each user)&lt;br /&gt;
* The X2Flow user interface is not available in the open source edition of X2Engine&lt;br /&gt;
&lt;br /&gt;
In cases where the remote end, which will receive data from X2Engine, can be modified with custom code, web hooks are a method of &amp;quot;subscribing&amp;quot; to events in X2Engine via the API. Whenever an event would happen in X2Engine, X2Engine will submit payload data to a return URL specified in the original webhook request, as JSON-encoded data in the request body.&lt;br /&gt;
&lt;br /&gt;
== Creating Web Hooks ==&lt;br /&gt;
To create a hook with an event that depends on a model type (i.e. contact updated vs. account updated), send a JSON-encoded list of hook attributes via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; to the following URI:&lt;br /&gt;
  index.php/api2/{_class}/hooks&lt;br /&gt;
Or, to create a web hook associated with a generic event (that does not depend on model type), or a model of determined/unambiguous type (i.e. &amp;quot;Action Complete&amp;quot;):&lt;br /&gt;
  index.php/api2/hooks&lt;br /&gt;
&lt;br /&gt;
The POST-ed JSON-encoded dictionary object should contain the following properties: &lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;event&amp;lt;/tt&amp;gt;&lt;br /&gt;
| An event name; see [[#Events Reference]]&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;target_url&amp;lt;/tt&amp;gt;&lt;br /&gt;
| The remote URL to receive the payload&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt;&lt;br /&gt;
| (optional): a 1 or 0 (or true/false). See [[#Interpreting Payload Data]]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Upon successful creation of a web hook, note that the server will respond with a '''201''' status. The body will be the JSON-encoded attributes of the new hook record, and the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header of the response will be set to a URL that can be used to remove the web hook when it is no longer needed (see [[#Deleting Web Hooks]]).&lt;br /&gt;
&lt;br /&gt;
== Supported Event Names and X2Flow ==&lt;br /&gt;
Events for which web hooks can be created are all named after X2Flow trigger class names. Trigger classes (whose files are named after them, like all other class files) are stored in the directory&lt;br /&gt;
  protected/components/x2flow/triggers&lt;br /&gt;
&lt;br /&gt;
In fact, any time that &amp;lt;tt&amp;gt;[[x2propdoc:X2Flow.html#_trigger|X2Flow::trigger]]&amp;lt;/tt&amp;gt; is called, a corresponding call to &amp;lt;tt&amp;gt;[[x2propdoc:ApiHook.html#_runAll|ApiHook::runAll]]&amp;lt;/tt&amp;gt; is also made, to execute all web hooks associated with that trigger event. The payload that is sent for all web hooks corresponding to that event is based on the value of the &amp;lt;tt&amp;gt;$params&amp;lt;/tt&amp;gt; argument that is sent to &amp;lt;tt&amp;gt;X2Flow::trigger&amp;lt;/tt&amp;gt;. In all cases, the payload is first converted to a pure array, i.e. not containing any objects or resource handles, so that it can be JSON-encoded and sent to the web hook target URL. The exact payload data will differ depending on the action; see &amp;quot;[[#Interpreting Payload Data]]&amp;quot; (coming soon) for further information.&lt;br /&gt;
&lt;br /&gt;
== Interpreting Payload Data ==&lt;br /&gt;
When you create a hook, you will have the option of setting its &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; property to 1. A value of 0/false (the default value) for this field implies that if one of the trigger parameters is named &amp;quot;model&amp;quot; and is a subclass of X2Model (i.e. contact, account, opportunity), the (JSON-encoded) payload data will contain a property &amp;lt;tt&amp;gt;'''resource_url'''&amp;lt;/tt&amp;gt; pointing to the URL on X2Engine of that model record. Thus, upon receiving the webhook request from X2Engine, the client end should then make a &amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt; request from that URL to retrieve the model part of the payload data. If, on the other hand, &amp;lt;tt&amp;gt;directPayload&amp;lt;/tt&amp;gt; is set to 1/true, what will instead happen is that the model will be included in the payload's &amp;lt;tt&amp;gt;'''model'''&amp;lt;/tt&amp;gt; property as a dictionary of attribute-value pairs.&lt;br /&gt;
&lt;br /&gt;
Note, the two most common possible properties &amp;lt;tt&amp;gt;resource_url&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;model&amp;lt;/tt&amp;gt; are mutually exclusive; which one if any will get included depends on the &amp;lt;tt&amp;gt;direcPayload&amp;lt;/tt&amp;gt;, other properties of the payload include but may not be limited to the following:&lt;br /&gt;
&lt;br /&gt;
The class of model (and thus the structure of data) that should be expected to come from any given web hook generally depends on the model class to which the event corresponds. For example, the model is of class &amp;lt;tt&amp;gt;Actions&amp;lt;/tt&amp;gt; in an &amp;lt;tt&amp;gt;ActionCompleteTrigger&amp;lt;/tt&amp;gt; event, but they could be any general record type (contacts, actions, accounts, etc.) in a &amp;lt;tt&amp;gt;RecordCreateTrigger&amp;lt;/tt&amp;gt; event.&lt;br /&gt;
&lt;br /&gt;
== Events Reference ==&lt;br /&gt;
'''As of version 4.1.1''', the following trigger events are available in X2Flow:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Event&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Description&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Payload&lt;br /&gt;
|-&lt;br /&gt;
|ActionCompleteTrigger&lt;br /&gt;
|An action has been marked as complete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who completed the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionOverdueTrigger&lt;br /&gt;
|An action is overdue (cron required)&lt;br /&gt;
|'''model'''/'''resource_url''': an action; '''duration''': the amount of time that has passed since the due date of the action&lt;br /&gt;
|-&lt;br /&gt;
|ActionUncompleteTrigger&lt;br /&gt;
|An action has been marked as uncomplete&lt;br /&gt;
|'''model'''/'''resource_url''': An action; '''user''': username of the user who marked the action as uncomplete&lt;br /&gt;
|-&lt;br /&gt;
|CampaignEmailClickTrigger&lt;br /&gt;
|A tracking link in a campaign has been clicked&lt;br /&gt;
|'''model''': the contact who clicked the link in their campaign email; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignUnsubscribeTrigger&lt;br /&gt;
|A contact has unsubscribed from email campaigns&lt;br /&gt;
|'''model''': the contact; '''campaign''': Name of the email campaign&lt;br /&gt;
|-&lt;br /&gt;
|CampaignWebActivityTrigger&lt;br /&gt;
|''(Professional Edition only)'' A contact who was part of a campaign is visiting your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''campaign''': Name of the email campaign; '''url''': URL of the page that the contact is visiting&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailClickTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has clicked a tracking link&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign; '''url''': URL visited by the subscriber&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterEmailOpenTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has opened an email&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|NewsletterUnsubscribeTrigger&lt;br /&gt;
|''(Professional Edition only)'' A newsletter subscriber has unsubscribed&lt;br /&gt;
|'''item''': The list item record (&amp;lt;tt&amp;gt;{&amp;quot;emailAddress&amp;quot;:&amp;lt;email address&amp;gt;,&amp;quot;opened&amp;quot;:&amp;lt;timestamp opened&amp;gt;,&amp;quot;clicked&amp;quot;:&amp;lt;timestamp clicked&amp;gt;,&amp;quot;unsubscribed&amp;quot;:&amp;lt;has unsubscribed&amp;gt;}&amp;lt;/tt&amp;gt;); '''email''': email address of subscriber; '''campaign''': name of the newsletter campaign&lt;br /&gt;
|-&lt;br /&gt;
|RecordCreateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been created&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordDeleteTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been deleted&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was inserted&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagAddTrigger&lt;br /&gt;
|A record has been tagged&lt;br /&gt;
|'''model'''/'''resource_url''': The record that was tagged; '''tags''': an array of strings (tags) that were added.&lt;br /&gt;
|-&lt;br /&gt;
|RecordTagRemoveTrigger&lt;br /&gt;
|Tags have been deleted from a record&lt;br /&gt;
|'''model'''/'''resource_url''': The record whose tags were changed; '''tags''': an array of strings (tags) that were removed&lt;br /&gt;
|-&lt;br /&gt;
|RecordUpdateTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been updated&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was updated&lt;br /&gt;
|-&lt;br /&gt;
|RecordViewTrigger&lt;br /&gt;
|A record of one of the main types (contact, action, account, opportunity, etc.) has been viewed&lt;br /&gt;
|'''model'''/'''resource_url''': The data that was viewed&lt;br /&gt;
|-&lt;br /&gt;
|TargetedContentRequestTrigger&lt;br /&gt;
|''(Professional Edition Only)'' Targeted content is being accessed on your website&lt;br /&gt;
|'''model'''/'''resource_url''': The contact on your website; '''url''': the URL being viewed; '''flowId''': internal parameter (not of much use outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|UserLoginTrigger&lt;br /&gt;
|A user has logged in&lt;br /&gt;
|'''user''': the username of the user who has logged in&lt;br /&gt;
|-&lt;br /&gt;
|UserLogoutTrigger&lt;br /&gt;
|A user has logged out&lt;br /&gt;
|'''user''': the username of the user who has logged out&lt;br /&gt;
|-&lt;br /&gt;
|WebActivityTrigger&lt;br /&gt;
|''(Professional Edition Only)'' A contact is viewing your website&lt;br /&gt;
|'''model'''/'''resource_url''': the contact; '''url''': the URL being viewed; '''probability''' ''(Platinum Edition only)'' percentage confidence of browser-fingerprinting-based match between a web client and a contact&lt;br /&gt;
|-&lt;br /&gt;
|WebleadTrigger&lt;br /&gt;
|A new web lead has come in&lt;br /&gt;
|'''model'''/'''resource_url''': the contact associated with the form submission; '''tags''': a list of tags added to the contact through the submission, if applicable &lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteStageTrigger&lt;br /&gt;
|A process stage has been completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine); '''stageNumber''': the number of the stage in the process that was completed.&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowCompleteTrigger&lt;br /&gt;
|A process has been fully completed&lt;br /&gt;
|'''workflow''': The workflow template model (has properties name, isDefault, lastUpdated and colors); '''model'''/'''resource_url''': the record (i.e. Opportunity/Contact) for which the stage was completed; '''workflowId''': internal use (not very useful outside X2Engine)&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowRevertStageTrigger&lt;br /&gt;
|A process stage has been reverted.&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartStageTrigger&lt;br /&gt;
|A process stage has been started&lt;br /&gt;
|Same as WorkflowCompleteStageTrigger&lt;br /&gt;
|-&lt;br /&gt;
|WorkflowStartTrigger&lt;br /&gt;
|A process has been started&lt;br /&gt;
|Same as WorkflowCompleteTrigger&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Deleting Web Hooks ==&lt;br /&gt;
Given the numeric identifier of a web hook, it can be deleted by sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; to a URI formatted as follows:&lt;br /&gt;
  index.php/api2/hooks/:{_id}&lt;br /&gt;
So, for a web hook with id=74:&lt;br /&gt;
  index.php/api2/hooks/:74&lt;br /&gt;
&lt;br /&gt;
The full URL should have been given to the API client in the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header in the response to the original web hook creation request. Upon successful deletion, the server will respond with status code '''204'''.&lt;br /&gt;
&lt;br /&gt;
= Interpreting Server Responses =&lt;br /&gt;
&lt;br /&gt;
== HTTP Response Codes ==&lt;br /&gt;
It is important to be able to read and interpret status codes (and for that matter, response headers) because in all success scenarios, the API does not respond with data envelopes. By this it is meant the act of wrapping a data model's attributes inside another array containing metadata about the status of the request. This is in effort to streamline and make more elegant code that handles response data. It is also for consistency's sake. A contacts model accessed as a resource object with name ending with &amp;lt;tt&amp;gt;.json&amp;lt;/tt&amp;gt; is expected to be ''that contact'', and not rather an array with server response info or other metadata.&lt;br /&gt;
&lt;br /&gt;
In general, the meanings of response codes closely or exactly follow the formal definitions as defined in [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html RFC2616], as well as the informal definitions of unconventional status codes (See [[wikipedia:List of HTTP status codes]]).  The following table lists each of the possible status codes that the API will respond with, the contexts in which they would appear, and what they indicate.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Code&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Request type or usage scenario&lt;br /&gt;
! scope=&amp;quot;col&amp;quot; | Meaning&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Success''' (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; &lt;br /&gt;
Also, when adding tags via &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''OK.'' General success status&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;201&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Created.'' A record was created; see the value of the &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; response header for its URL in the API&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;204&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''No content.'' An action was completed but the server does not need to return any content. The body of the response will be empty.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Redirection''' (&amp;lt;tt&amp;gt;3XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;300&amp;lt;/tt&amp;gt;&lt;br /&gt;
|When using the &amp;quot;[[#Direct Manipulation by Attributes|find by attributes]]&amp;quot; direct access URI&lt;br /&gt;
|''Multiple choices.'' There criteria specified for direct access (as opposed to querying) match more than one record.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;303&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;&lt;br /&gt;
When requesting an object that exists but is not actually associated with the record specified in the request &lt;br /&gt;
|''See other.'' The requested resource is somewhere else. The &amp;lt;tt&amp;gt;Location&amp;lt;/tt&amp;gt; header should contain the correct, full URL to the resource.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Client Error''' (&amp;lt;tt&amp;gt;4XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;400&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Invalid request.'' General status code for when something is missing, malformed or incorrect in the request headers/body.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;401&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|Authentication failure or missing authentication credentials.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;403&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Forbidden.'' This may be because the user in API authentication does not actually have permission in X2Engine to perform the specified action. In Platinum Edition, this code can also indicate that the IP address of the API client has been blocked.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;404&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions.&lt;br /&gt;
|''Not found,'' or invalid URI. It is used whenever a specified record to retrieve directly does not exist, but also as a catch-all for a location that the API was not configured to interpret.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;405&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Method not allowed.'' The URI is not malformed, but the method of the request being sent to the server is not permitted. An example would be sending a POST request, which is intended for creating new records, to the URI of an existing record, or sending a &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt; request to a &amp;quot;read-only&amp;quot; resource.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;408&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request timeout.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;413&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All methods; server-generated&lt;br /&gt;
|''Request entity too large.'' This code is not as of this writing not produced by the API; it might be returned by the web server itself.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;415&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Unsupported media type.'' As of this writing, this happens only whenever the request is sent with a body and the &amp;lt;tt&amp;gt;Content-Type&amp;lt;/tt&amp;gt; header is not set to &amp;quot;application/json&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;422&amp;lt;/tt&amp;gt;&lt;br /&gt;
|&amp;lt;tt&amp;gt;PATCH&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;PUT&amp;lt;/tt&amp;gt;&lt;br /&gt;
When creating or updating a record&lt;br /&gt;
|''Unprocessable entity.'' This always indicates a data validation error when attempting to set fields of and save an active record model. The client is expected to resolve these issues and resubmit the data with all of the fields formatted in such a way that it is acceptable.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;429&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Platinum Edition only''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Too many requests.'' When rate limiting is enabled in the API settings, the server will use this code to indicate that the API client has been making requests to the API too frequently. When this status is sent, the response should also contain a &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; header specifying the number of seconds to wait before trying again.&lt;br /&gt;
&lt;br /&gt;
There is one exception to this, however: when attempting to create a web hook for a given user, event and model name, if the hook limit has already been reached for a user, event and model name, a 429 without &amp;lt;tt&amp;gt;Retry-After&amp;lt;/tt&amp;gt; would be sent, and this implies that no more hooks for that combination should be created.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | '''Server Error''' (&amp;lt;tt&amp;gt;5XX&amp;lt;/tt&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;500&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Internal server error.'' General status for when something isn't right on the server. PHP and database errors will produce this status code.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;501&amp;lt;/tt&amp;gt;&lt;br /&gt;
|All request methods/actions&lt;br /&gt;
|''Not implemented.'' The API should use this code to denote that a given method, action, etc. is not yet available, but may be in the future.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row&amp;quot; | &amp;lt;tt&amp;gt;503&amp;lt;/tt&amp;gt;&lt;br /&gt;
|''Professional and Platinum Editions''&lt;br /&gt;
All request methods/actions&lt;br /&gt;
|''Unavailable.'' X2Engine and/or its API service has been disabled/locked by an administrator.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Error Objects ==&lt;br /&gt;
In all error responses produced by the API and not by the server itself, the body of the response will be a JSON containing metadata about the error and the response. This is for compatibility with less-than-satisfactory client libraries which cannot read the actual response code or headers, but might be able to read the response body when the code is not in the success (&amp;lt;tt&amp;gt;2XX&amp;lt;/tt&amp;gt;) range. In such cases, the response will be a JSON-encoded object with at least the following properties:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | httpHeaders&lt;br /&gt;
|A dictionary of HTTP headers that were set deliberately by the API (as opposed to automatically, by the web server software) in the response.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | message&lt;br /&gt;
|A general message about what happened and why. This will in most cases also be saved in the API log.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | error&lt;br /&gt;
|Boolean true/false; will generally be true.&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | status&lt;br /&gt;
|The HTTP status code that was sent in the response header&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
In some cases, the response JSON may include the following additional properties:&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! scope=&amp;quot;row | errors&lt;br /&gt;
| A dictionary of validation errors encountered when attempting to save an active record model; included with responses of status code '''422'''.&lt;br /&gt;
The structure will be indexed by attribute name, each entry containing a list of applicable validation errors. It is essentially the resulting value of the [[yii:CModel#errors-detail|CModel.errors]] property.&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy)&amp;diff=1141</id>
		<title>Web Lead Capture via API (legacy)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy)&amp;diff=1141"/>
				<updated>2013-11-05T21:55:14Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* The Basics */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
'''This version the Web Lead API is only compatible with X2CRM versions at or after 3.6'''&lt;br /&gt;
&lt;br /&gt;
Much of what was written in the [[Web Lead API (legacy)|old version]] is the same, but there are some fairly key differences, namely the way our web tracker (professional feature) works. This will be addressed in its own section, but the back-end of it will be included in the capture form documentation.&lt;br /&gt;
&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script can go anywhere you want as the new web tracker is much more flexible than the old version.''' You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
You'll also want to include the tracking key, if it exists. This is now provided up front instead of returned after creation, so this code should just be a few extra lines before you submit the data to the API.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if(isset($_POST['x2_key'])){&lt;br /&gt;
    $contact-&amp;gt;trackingKey=$_POST['x2_key'];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(isset($_POST['x2_key'])){&lt;br /&gt;
    $contact-&amp;gt;trackingKey=$_POST['x2_key'];&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
Also, you may have noticed that $_POST['x2_key'] was referenced, but it was not explained where we got that from. Please see the [[#Web Tracker Front End|Web Tracker Front End]] section for an explanation.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Web Tracker Front End===&lt;br /&gt;
The new web tracker is significantly more reliable and flexible to work with. However, you'll need to make a slight change to your HTML form to make it work. What you'll want to do is create a hidden input in your form with the name &amp;quot;x2_key&amp;quot; and that's it! Our web tracker JS will automatically detect this field and fill it with the correct tracking key.&lt;br /&gt;
&lt;br /&gt;
The advantages of the new JS based web tracker are numerous, but the biggest one is that you no longer need an IFrame to your X2 installation! The tracker tracks on your web servers domain now, which removed a lot of the hassle involved with getting the tracker to function properly.&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy)&amp;diff=1140</id>
		<title>Web Lead Capture via API (legacy)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy)&amp;diff=1140"/>
				<updated>2013-11-05T21:54:27Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
'''This version the Web Lead API is only compatible with X2CRM versions at or after 3.6'''&lt;br /&gt;
&lt;br /&gt;
Much of what was written in the [[Web Lead API (legacy)|old version]] is the same, but there are some fairly key differences, namely the way our web tracker (professional feature) works. This will be addressed in its own section, but the back-end of it will be included in the capture form documentation.&lt;br /&gt;
&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script can go anywhere you want as the new web tracker is much more flexible than the old version.''' You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
You'll also want to include the tracking key, if it exists. This is now provided up front instead of returned after creation, so this code should just be a few extra lines before you submit the data to the API.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if(isset($_POST['x2_key'])){&lt;br /&gt;
    $contact-&amp;gt;trackingKey=$_POST['x2_key'];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(isset($_POST['x2_key'])){&lt;br /&gt;
    $contact-&amp;gt;trackingKey=$_POST['x2_key'];&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
Also, you may have noticed that $_POST['x2_key'] was referenced, but it was not explained where we got that from. Please see the Web Tracker Front End section for an explanation.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Web Tracker Front End===&lt;br /&gt;
The new web tracker is significantly more reliable and flexible to work with. However, you'll need to make a slight change to your HTML form to make it work. What you'll want to do is create a hidden input in your form with the name &amp;quot;x2_key&amp;quot; and that's it! Our web tracker JS will automatically detect this field and fill it with the correct tracking key.&lt;br /&gt;
&lt;br /&gt;
The advantages of the new JS based web tracker are numerous, but the biggest one is that you no longer need an IFrame to your X2 installation! The tracker tracks on your web servers domain now, which removed a lot of the hassle involved with getting the tracker to function properly.&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy)&amp;diff=1139</id>
		<title>Web Lead Capture via API (legacy)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy)&amp;diff=1139"/>
				<updated>2013-11-05T21:51:45Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: Created page with &amp;quot;Category:Development == Introduction == '''This version the Web Lead API is only compatible with X2CRM versions at or after 3.6'''  Much of what was written in the old ver...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
'''This version the Web Lead API is only compatible with X2CRM versions at or after 3.6'''&lt;br /&gt;
&lt;br /&gt;
Much of what was written in the old version is the same, but there are some fairly key differences, namely the way our web tracker (professional feature) works. This will be addressed in its own section, but the back-end of it will be included in the capture form documentation.&lt;br /&gt;
&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script can go anywhere you want as the new web tracker is much more flexible than the old version.''' You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
You'll also want to include the tracking key, if it exists. This is now provided up front instead of returned after creation, so this code should just be a few extra lines before you submit the data to the API.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if(isset($_POST['x2_key'])){&lt;br /&gt;
    $contact-&amp;gt;trackingKey=$_POST['x2_key'];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(isset($_POST['x2_key'])){&lt;br /&gt;
    $contact-&amp;gt;trackingKey=$_POST['x2_key'];&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
Also, you may have noticed that $_POST['x2_key'] was referenced, but it was not explained where we got that from. Please see the Web Tracker Front End section for an explanation.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Web Tracker Front End===&lt;br /&gt;
The new web tracker is significantly more reliable and flexible to work with. However, you'll need to make a slight change to your HTML form to make it work. What you'll want to do is create a hidden input in your form with the name &amp;quot;x2_key&amp;quot; and that's it! Our web tracker JS will automatically detect this field and fill it with the correct tracking key.&lt;br /&gt;
&lt;br /&gt;
The advantages of the new JS based web tracker are numerous, but the biggest one is that you no longer need an IFrame to your X2 installation! The tracker tracks on your web servers domain now, which removed a lot of the hassle involved with getting the tracker to function properly.&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_API&amp;diff=1138</id>
		<title>Web Lead API</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_API&amp;diff=1138"/>
				<updated>2013-11-05T19:14:56Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: Jake moved page Web Lead API to Web Lead API (legacy): No longer supported as of version 3.6&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Web Lead API (legacy)]]&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1137</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1137"/>
				<updated>2013-11-05T19:14:55Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: Jake moved page Web Lead API to Web Lead API (legacy): No longer supported as of version 3.6&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
'''This version the Web Lead API is only compatible with X2CRM versions at or before 3.5.6'''&lt;br /&gt;
&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. The tracking cookie is used for the web tracker which is a part of our professional edition. Even if you're running open source it might be worth setting this so you can have the cookies stored in case you upgrade in the future. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1136</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1136"/>
				<updated>2013-11-05T19:14:17Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
'''This version the Web Lead API is only compatible with X2CRM versions at or before 3.5.6'''&lt;br /&gt;
&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. The tracking cookie is used for the web tracker which is a part of our professional edition. Even if you're running open source it might be worth setting this so you can have the cookies stored in case you upgrade in the future. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1135</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1135"/>
				<updated>2013-11-05T19:14:07Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
'''This version the Web Lead API is only compatible with X2CRM versions &amp;lt;= 3.5.6'''&lt;br /&gt;
&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. The tracking cookie is used for the web tracker which is a part of our professional edition. Even if you're running open source it might be worth setting this so you can have the cookies stored in case you upgrade in the future. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1074</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1074"/>
				<updated>2013-09-30T23:09:31Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. The tracking cookie is used for the web tracker which is a part of our professional edition. Even if you're running open source it might be worth setting this so you can have the cookies stored in case you upgrade in the future. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
The basic script doesn't have any sort of handling for finding people who submit the form more than once. Instead of simply creating duplicate records, you can do a few little tricks to make this part of the lead script smarter. First things first, we need a way to check for a duplicate Contact. Before you set any attributes on the Contact model which is the code that has: &amp;lt;tt&amp;gt;foreach($attributes as $key=&amp;gt;$value){&amp;lt;/tt&amp;gt; you'll want to add this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;email=$attributes['email'];&lt;br /&gt;
$contact-&amp;gt;contactLookup();&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  // Code to handle duplicates here.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
First, we set the email of the Contact because that's the primary way to look for duplicates. Then we call the &amp;quot;contactLookup&amp;quot; function which does exactly what it says--looks up a Contact. If we can't find any, the response code will be a 404. So all we do then is make our if statement check if the response was something other than a 404 and handle the duplicate in here! There's a few different ways we can deal with that. '''All of the code in the following blocks starts at the response code check.''' The first is a simple redirect, ignoring the duplicate record, which would look like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  Header(&amp;quot;Location: host.domain&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Replacing &amp;quot;host.domain&amp;quot; with your own redirect location. The next step is a little more advanced, which would be to instead detect empty fields in the Contact record and fill them with any submitted data, while preserving data already on the record. The code to do that would look something more like this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if($contact-&amp;gt;responseCode!='404'){&lt;br /&gt;
  foreach($attributes as $key =&amp;gt; $value){ // Try to set any empty attributes&lt;br /&gt;
        if(isset($fieldMap[$key]) &amp;amp;&amp;amp; empty($contact-&amp;gt;{$fieldMap[$key]})){ // Check if value is empty + found in field map&lt;br /&gt;
            $contact-&amp;gt;{$fieldMap[$key]} = $value; // Found in field map, used mapped attribute&lt;br /&gt;
        }elseif(empty($contact-&amp;gt;$key)){ // Just check if value is empty&lt;br /&gt;
            $contact-&amp;gt;$key = $value; // No match in field map, assume it's a Contact attribute&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $contact-&amp;gt;contactUpdate();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1073</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1073"/>
				<updated>2013-09-30T19:51:17Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* Final Product */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. The tracking cookie is used for the web tracker which is a part of our professional edition. Even if you're running open source it might be worth setting this so you can have the cookies stored in case you upgrade in the future. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Advanced Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1072</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1072"/>
				<updated>2013-09-30T19:51:03Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. The tracking cookie is used for the web tracker which is a part of our professional edition. Even if you're running open source it might be worth setting this so you can have the cookies stored in case you upgrade in the future. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
TODO: Add this section&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
=== Lead Logging ===&lt;br /&gt;
=== Custom Redirects ===&lt;br /&gt;
== Final Product ==&lt;br /&gt;
With all the bells and whistles!&lt;br /&gt;
TODO: Add this&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1071</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1071"/>
				<updated>2013-09-27T23:56:52Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* The Basics */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map, configure the APIModel constructor, and replace URLs properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
TODO: Add this section&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1070</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1070"/>
				<updated>2013-09-27T23:36:52Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that's it! For a basic web lead capture script, this is all we need. The code listed here should be totally functional to copy and paste over to your own server, as long you define your own field map properly. However, it's pretty basic and there's at least one extra feature that might be required to work correctly.&lt;br /&gt;
&lt;br /&gt;
== Advanced Topics ==&lt;br /&gt;
=== Duplicate Handling ===&lt;br /&gt;
=== Adding Tags ===&lt;br /&gt;
TODO: Add this section&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1069</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1069"/>
				<updated>2013-09-27T23:32:08Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1068</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1068"/>
				<updated>2013-09-27T23:31:54Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;br /&gt;
&lt;br /&gt;
The last component of the custom form is submitting the data, setting the tracking cookie, and redirecting. You can do these like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And be sure to replace '/path/to/x2' with your web path, 'host.domain' with your server URL, and the redirect with whatever URL you want visitors to be sent to after filling out the form. Now, if we put it all together, the final product should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$contact-&amp;gt;contactCreate(); // Call API to create contact&lt;br /&gt;
if(!empty($contact-&amp;gt;trackingKey)){&lt;br /&gt;
    setcookie('x2_key',$contact-&amp;gt;trackingKey,time()+31536000,'/path/to/x2','host.domain'); // Set cookie&lt;br /&gt;
}&lt;br /&gt;
Header('Location: host.domain/redirect'); // Redirect to homepage&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1067</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1067"/>
				<updated>2013-09-27T23:18:22Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's about it for now. The first thing we do is include the APIModel class, which will allow for easy use of X2's API. This class is heavily documented and provides a variety of wrapper methods for common X2 API functions. It also behaves in many ways just like a regular model, so if you have any X2 development experience you'll likely feel right at home. &lt;br /&gt;
&lt;br /&gt;
The next steps in this process are to initialize the API Model correctly. To do that, you'll need three things:&lt;br /&gt;
* The username of a user with permission to create Contacts&lt;br /&gt;
* The user API key of that same user&lt;br /&gt;
* The URL of your X2 installation.&lt;br /&gt;
&lt;br /&gt;
The first is easy to obtain, and I recommend using &amp;quot;admin&amp;quot; as it's simpler. From there, go to the &amp;quot;Users&amp;quot; page, click on &amp;quot;admin&amp;quot; in the grid, and select &amp;quot;Update User&amp;quot; from the left sidebar menu. You should see a field called API Key. You can set this to whatever you want, but they're randomly generated upon user creation. For example, mine on my development server at the time of this writing is &amp;quot;7uoHGRIBlb0lKym56ieiDf3c7idzCCP7&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The API Model takes 3 parameters for its constructor. You may be able to guess that they are the username, API key, and URL of the server. So now our code should look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That's the most important step to get right. If you don't properly set up your API model, all of the requests will fail and this whole process will be useless. '''Please double check you have entered in the information correctly.'''&lt;br /&gt;
&lt;br /&gt;
Now we need to be able to set the data of our Contact. Most pre-built forms do not follow the same naming conventions of the database columns in X2. As such, I like to build a field map that will translate the information in the POST data into usable information by X2. If all your field names match ours exactly, then this step isn't required and you can simply say &amp;lt;tt&amp;gt;$contact-&amp;gt;attributes = $attributes;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
require 'APIModel.php';&lt;br /&gt;
$attributes = $_POST;&lt;br /&gt;
$contact = new APIModel('admin','7uoHGRIBlb0lKym56ieiDf3c7idzCCP7','www.host.domain/path/to/x2');&lt;br /&gt;
$fieldMap = array( // This map should be of the format 'your_fieldname'=&amp;gt;'x2_fieldname',&lt;br /&gt;
    'first_name'=&amp;gt;'firstName',&lt;br /&gt;
    'last_name'=&amp;gt;'lastName',&lt;br /&gt;
    'mobile'=&amp;gt;'phone2',&lt;br /&gt;
    'information'=&amp;gt;'backgroundInfo',&lt;br /&gt;
);&lt;br /&gt;
foreach($attributes as $key=&amp;gt;$value){&lt;br /&gt;
   if(isset($fieldMap[$key])){&lt;br /&gt;
        $contact-&amp;gt;{$fieldMap[$key]}=$value; // Found in field map, used mapped attribute&lt;br /&gt;
    }else{&lt;br /&gt;
        $contact-&amp;gt;$key=$value; // No match anywhere, assume it's a Contact attribute&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will now loop through your POST data and set them to the Contact attributes based on your field map. Note that if a match isn't found in the map, it assumes the names are the same in your form as they are in X2.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1066</id>
		<title>Web Lead Capture via API (legacy; pre-3.5.6)</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Web_Lead_Capture_via_API_(legacy;_pre-3.5.6)&amp;diff=1066"/>
				<updated>2013-09-27T23:03:03Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: Created page with &amp;quot;Category:Development == Introduction == Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to n...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
== Introduction ==&lt;br /&gt;
Our web lead from editor allows for some solid forms that the average user can put on their website without much issue. Little to no development knowledge is required, and the form integrates perfectly with X2 from a web lead standpoint as well as our web tracker. But for some users, this isn't enough. Many websites already have forms in use that they don't want to sacrifice or change, but still want all of the features of X2 integration. The good news is that with some basic development knowledge this is a very doable project. This article will walk you through setting up a custom web lead capture script which will take POST data from your web lead form and enter it into X2 via API. The later sections of the article will cover some advanced customizations to make the form work better for you.&lt;br /&gt;
&lt;br /&gt;
== The Basics ==&lt;br /&gt;
The first thing you'll need to do is create some form of capture script, and call it whatever you like. I tend to use things like &amp;quot;contactForm&amp;quot; to be very clear what it is. '''This script should go onto the same webserver as your X2CRM installation''' so that cookies can be properly set, but it doesn't need to be in the same folder. You'll also need to make sure that the &amp;quot;APIModel.php&amp;quot; class file is somewhere accessible. This file is provided in protected/models/ of your X2 installation, and can be copied to anywhere you like. So, what does this capture script currently look like?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
Test&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Customization_Framework&amp;diff=1015</id>
		<title>Customization Framework</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Customization_Framework&amp;diff=1015"/>
				<updated>2013-08-26T20:34:55Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: /* Custom App Configuration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
X2CRM's source code is completely transparent and totally under your control. There's no need to compiled PHP, so altering your software is a simply a matter of hacking around in the files. Traditionally, this meant directly altering the source code within X2Engine's file structure. But this could be problematic to keep track of, given the vast number of files. On top of that, automatic updates to any of those files would wipe out all of your changes, so you would have to choose between customization and receiving updates.&lt;br /&gt;
&lt;br /&gt;
X2Engine provides a means to alter classes and templates without modifying the original source files. You can create an alternate version of any PHP file, while preserving the original, which makes it much easier to keep track of your changes as well as preserving your alterations through updates. You may still need to rework your custom code when an update is incompatible, but it will be much easier.&lt;br /&gt;
&lt;br /&gt;
= Overriding Files =&lt;br /&gt;
[[File:Custom_folder.gif|right]]&lt;br /&gt;
Practically any php file used in X2Engine can be overridden. X2Engine's customization framework resides in the &amp;lt;tt&amp;gt;/custom&amp;lt;/tt&amp;gt; directory, which mirrors the structure of the source code. For example, &amp;lt;tt&amp;gt;/custom/protected/components&amp;lt;/tt&amp;gt; corresponds to &amp;lt;tt&amp;gt;/protected/components&amp;lt;/tt&amp;gt;. Any php file placed in &amp;lt;tt&amp;gt;/custom&amp;lt;/tt&amp;gt; will be used instead of the original.&lt;br /&gt;
{{clear}}&lt;br /&gt;
= Extending Controllers =&lt;br /&gt;
X2Engine is built on Yii, which uses MVC (model-view-controller) architecture. Overriding an entire file is practical when dealing with views or smaller classes (such as models), but controllers can present a greater challenge. It's only a matter of time before we update one of the 1500+ lines of code in a controller. This means you have to manually find and transfer your changes from the old version, which is time-consuming. To remedy this, we allow substitution of any controller in X2Engine with an extended class.&lt;br /&gt;
&lt;br /&gt;
Whenever a controller is loaded, X2Engine checks for the same filename with &amp;quot;My&amp;quot; at the beginning. For example, if you want to override a single action to ContactsController, you can create a file called MyContactsController in &amp;lt;tt&amp;gt;/custom/protected/modules/contacts/controllers&amp;lt;/tt&amp;gt; containing the following:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class MyContactsController extends ContactsController {&lt;br /&gt;
    function actionIndex() {&lt;br /&gt;
        echo 'Hello World!';&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This file will automatically be used instead of ContactsController and should still work if ContactsController is changed in an update. You may still need to manually merge changes if an update alters the same part of the controller that you changed, but it will be much easier to find.&lt;br /&gt;
&lt;br /&gt;
= Custom App Configuration =&lt;br /&gt;
The configuration for the Yii application can also be customized through the files &amp;quot;web.php&amp;quot;, &amp;quot;console.php&amp;quot; and &amp;quot;test.php&amp;quot; in &amp;lt;tt&amp;gt;custom/protected/config&amp;lt;/tt&amp;gt;. These files will be included and run in the default configuration files for X2CRM running as a web application, console application or unit testing environment (respectively). &lt;br /&gt;
&lt;br /&gt;
An array which mirrors the modified parts of main.php (the default configuration file) should be returned within the custom config file. To customize the configuration of the application, i.e. to add parameters to &amp;lt;tt&amp;gt;Yii::app()-&amp;gt;params&amp;lt;/tt&amp;gt;, one only need add/remove/alter the values and keys within &amp;lt;tt&amp;gt;$config&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Known Issues =&lt;br /&gt;
* Currently, only PHP files can be substituted. To change a CSS or Javascript file you would have to edit the original file.&lt;br /&gt;
&lt;br /&gt;
* There is no guarantee a customized installation will still work after an update. You will still have to manually merge changes if you want (or need) to use an updated version of a customized file. Depending on the file, you may have to do this for most automatic updates (since some files are changed far more often than others). Luckilly, most of our changes are usually in controller files, which can be extended rather than just replaced, so the updates are less likely to be incompatible with your modifications.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1014</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1014"/>
				<updated>2013-08-23T21:19:37Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria. Obviously this is the only method of interaction with the database through an administrative interface like phpMyAdmin or Virtualmin, and no parameter binding is available. &lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
Raw SQL can (for the most part) be used in 3 ways within X2CRM. The first two are both similar and are exclusively ways of accessing rows of data from the database. These are CSqlDataProvider, and the Model's findBySql method. The purposes of these examples are to show how to get effectively the same set of data using three different methods, even though this is not the ideal situation to use Raw SQL. Thus, some of these queries are excessively simplistic and do not return very useful data, but an example of how to use SQL in this context is demonstrated. &lt;br /&gt;
&lt;br /&gt;
'''CSqlDataProvider'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$count = Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT COUNT(*) FROM x2_contacts WHERE firstName IS NOT NULL')-&amp;gt;queryScalar();&lt;br /&gt;
$sql = &amp;quot;SELECT * FROM x2_contacts WHERE firstName IS NOT NULL&amp;quot;;&lt;br /&gt;
$dataProvider = new CSqlDataProvider($sql, array(&lt;br /&gt;
    'totalItemCount' =&amp;gt; $count,&lt;br /&gt;
    'sort' =&amp;gt; array(&lt;br /&gt;
        'attributes' =&amp;gt; array(&lt;br /&gt;
             'id', 'lastName', 'email',&lt;br /&gt;
        ),&lt;br /&gt;
    ),&lt;br /&gt;
    'pagination' =&amp;gt; array(&lt;br /&gt;
        'pageSize' =&amp;gt; 10,&lt;br /&gt;
    ),&lt;br /&gt;
));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will get a Data Provider populated with all records from the Contacts table where the First Name field is not blank. Parameters to be bound can be provided as a &amp;quot;params&amp;quot; array. The fields listed in the &amp;quot;sort&amp;quot; array specify which columns can be sorted on, and providing a &amp;quot;totalItemCount&amp;quot; is required for pagination. The CSqlDataProvider has a few places where it can be very useful, like for creating a Grid View of data from a table with no associated model. However, it is often easier to simply make a model and gain access to a lot of other features in the process.&lt;br /&gt;
&lt;br /&gt;
'''Find By SQL'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$sql = 'SELECT * FROM x2_contacts WHERE firstName IS NOT NULL';&lt;br /&gt;
$contacts = X2Model::model('Contacts')-&amp;gt;findAllBySql($sql);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will return an array of Contact models where the First Name field is not blank. As with the CSqlDataProvider, parameters can be bound as an optional second function parameter of findAllBySql. This is fairly simplistic and not really all that useful, unless you're looking to loop through and perform data operations on the models. There are better ways to do this particular task. However, these can be loaded into a CActiveDataProvider by calling its setData method on this array.&lt;br /&gt;
&lt;br /&gt;
'''CDbCommand'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$sql = 'SELECT * FROM x2_contacts WHERE firstName IS NOT NULL';&lt;br /&gt;
$contacts = Yii::app()-&amp;gt;db-&amp;gt;createCommand($sql)-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will return an array of arrays, where each secondary array is an associative array of Contact attributes. Parameters can be bound as an optional second function parameter of queryAll, the first being a flag of whether to return an associative array (which defaults to true). This is also not particularly useful because it will be difficult to get any data changes you make back into the database, and there are again better ways to obtain this kind of data. &lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
[[yii:CDbCommand|CDbCommand]] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Security. CDbCommand allows for parameter binding, which prevents SQL injection so long as you set up your command properly.&lt;br /&gt;
* Speed. Still fairly quick, as CDbCommand simply converts the command object into a string SQL query.&lt;br /&gt;
* Some complex queries like joins are easier to do with CDbCommand than Raw SQL.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Some simple queries like basic selects are more complicated than Raw SQL.&lt;br /&gt;
* Still requires some SQL knowledge, as most of the command methods are just aliases for SQL commands.&lt;br /&gt;
* Most of Yii's Data Providers / data display objects aren't inherently compatible with CDbCommands (though as of 1.1.13 CSqlDataProvider can take a CDbCommand).&lt;br /&gt;
&lt;br /&gt;
In general, I prefer to use CDbCommand only when there is no Model for the table I am working with, or if I only need 1-2 columns of data and loading the full model would be a waste of memory. It has its uses in some very specific situations but Models should usually be used in favor of this.&lt;br /&gt;
== Models ==&lt;br /&gt;
Models are the primary method of interaction with the database in X2CRM. All Models in the software extend one of two classes: [[x2doc:X2Model|X2Model]] or [[yii:CActiveRecord|CActiveRecord]]. X2Model is a special model class that extends CActiveRecord and is used for Models which have dynamic fields rather than a fixed set of attributes. Examples of this include Contacts, Accounts, and most of the major modules. CActiveRecord models are for tables with static field definitions and don't need the advanced functionality of X2Model. Examples of this include models like Fields, Profiles, and most non-major module related models. &lt;br /&gt;
&lt;br /&gt;
Models provide the easiest method of interaction with database records. For example, if we wanted to load a model into a form, allow a user to edit the data, and then update the database record, here is how it would be done in each of the three methods:&lt;br /&gt;
&lt;br /&gt;
'''Raw SQL''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT * FROM x2_contacts WHERE id='.$id)-&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$update='';&lt;br /&gt;
foreach($_POST['Contacts'] as $attribute=&amp;gt;$value){&lt;br /&gt;
    $update.=$attribute.&amp;quot;='&amp;quot;.$value.&amp;quot;', &amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if(!empty($update)){&lt;br /&gt;
    $update=substr($update,0,-2);&lt;br /&gt;
    Yii::app()-&amp;gt;db-&amp;gt;createCommand('UPDATE x2_contacts SET '.$updateQuery.' WHERE id='.$id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''CDbCommand'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;select('*')&lt;br /&gt;
    -&amp;gt;from('x2_contacts')&lt;br /&gt;
    -&amp;gt;where('id=:id', array(':id' =&amp;gt; $id))&lt;br /&gt;
    -&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;update('x2_contacts', $_POST['Contacts'], 'id=:id', array(':id' =&amp;gt; $id));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Model'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$model = X2Model::model('Contacts')-&amp;gt;findByPk($id);&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
$model-&amp;gt;save();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
As can be seen, the Model method is by far the simplest. What's more, none of the 3 listed methods above will actually work with the X2CRM codebase. This is because the way data is stored is not always the same as it is entered by or displayed to a user. For example, date fields display in a nice, localized format and are entered via a Date Picker widget. However, they are stored in the database as UNIX Timestamps for ease of conversion to other formats and mathematical operations. So simply setting &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
will not work, but X2Model has a special method written to handle cases like this. If we instead call&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;setX2Fields($_POST['Contacts']);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The Model class itself will properly format user input data into a database-friendly format. This is because information on each Model attribute is stored in the x2_fields table and can be easily accessed within the Model class. Performing a similar operation via CDbCommand or Raw SQL would be very lengthy and tedious. Models and X2Model in particular really deserve their own page, so an in-depth discussion on that will be left for elsewhere.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Ease of use. Models are incredibly easy to work with and require little to no SQL knowledge to use.&lt;br /&gt;
* Functionality. Models have a very large number of built in functions to make your life easier, and we've made even more of these available through X2Model and its related behaviors.&lt;br /&gt;
* Flexibility. Yii's code has an absolute ton of functionality designed to work with Models. Many, many things are available to load models, get lists of them and render those lists, etc. &lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Size. Models involve loading all of the data for a row from the database table, but have a lot of associated overhead. There are a large number of method calls and pieces of other information stored in the model that can increase overhead.&lt;br /&gt;
* Limited Purpose. Models are mostly intended to be a representation of a row of data, which is fantastic if that's what you need (and for most of what X2 does, it is) but for complicated or specific queries (especially ones collecting aggregate data or statistics) they are not particularly useful.&lt;br /&gt;
* Learning Curve. There are a lot of things Models can do, and it certainly took me a while to figure out a lot of the more interesting or obscure features and methods.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1013</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1013"/>
				<updated>2013-08-20T22:41:31Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
=== Examples ===&lt;br /&gt;
Raw SQL can (for the most part) be used in 3 ways within X2CRM. The first two are both similar and are exclusively ways of accessing rows of data from the database. These are CSqlDataProvider, and the Model's findBySql method.&lt;br /&gt;
&lt;br /&gt;
'''CSqlDataProvider'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$count = Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT COUNT(*) FROM x2_contacts WHERE firstName IS NOT NULL')-&amp;gt;queryScalar();&lt;br /&gt;
$sql = &amp;quot;SELECT * FROM x2_contacts WHERE firstName IS NOT NULL&amp;quot;;&lt;br /&gt;
$dataProvider = new CSqlDataProvider($sql, array(&lt;br /&gt;
    'totalItemCount' =&amp;gt; $count,&lt;br /&gt;
    'sort' =&amp;gt; array(&lt;br /&gt;
        'attributes' =&amp;gt; array(&lt;br /&gt;
             'id', 'lastName', 'email',&lt;br /&gt;
        ),&lt;br /&gt;
    ),&lt;br /&gt;
    'pagination' =&amp;gt; array(&lt;br /&gt;
        'pageSize' =&amp;gt; 10,&lt;br /&gt;
    ),&lt;br /&gt;
));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will get a Data Provider populated with all records from the Contacts table where the First Name field is not blank. Parameters to be bound can be provided as a &amp;quot;params&amp;quot; array. The fields listed in the &amp;quot;sort&amp;quot; array specify which columns can be sorted on, and providing a &amp;quot;totalItemCount&amp;quot; is required for pagination. The CSqlDataProvider has a few places where it can be very useful, like for creating a Grid View of data from a table with no associated model. However, it is often easier to simply make a model and gain access to a lot of other features in the process.&lt;br /&gt;
&lt;br /&gt;
'''Find By SQL'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
[[yii:CDbCommand|CDbCommand]] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Security. CDbCommand allows for parameter binding, which prevents SQL injection so long as you set up your command properly.&lt;br /&gt;
* Speed. Still fairly quick, as CDbCommand simply converts the command object into a string SQL query.&lt;br /&gt;
* Some complex queries like joins are easier to do with CDbCommand than Raw SQL.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Some simple queries like basic selects are more complicated than Raw SQL.&lt;br /&gt;
* Still requires some SQL knowledge, as most of the command methods are just aliases for SQL commands.&lt;br /&gt;
* Most of Yii's Data Providers / data display objects aren't inherently compatible with CDbCommands (though as of 1.1.13 CSqlDataProvider can take a CDbCommand).&lt;br /&gt;
&lt;br /&gt;
In general, I prefer to use CDbCommand only when there is no Model for the table I am working with, or if I only need 1-2 columns of data and loading the full model would be a waste of memory. It has its uses in some very specific situations but Models should usually be used in favor of this.&lt;br /&gt;
== Models ==&lt;br /&gt;
Models are the primary method of interaction with the database in X2CRM. All Models in the software extend one of two classes: [[x2doc:X2Model|X2Model]] or [[yii:CActiveRecord|CActiveRecord]]. X2Model is a special model class that extends CActiveRecord and is used for Models which have dynamic fields rather than a fixed set of attributes. Examples of this include Contacts, Accounts, and most of the major modules. CActiveRecord models are for tables with static field definitions and don't need the advanced functionality of X2Model. Examples of this include models like Fields, Profiles, and most non-major module related models. &lt;br /&gt;
&lt;br /&gt;
Models provide the easiest method of interaction with database records. For example, if we wanted to load a model into a form, allow a user to edit the data, and then update the database record, here is how it would be done in each of the three methods:&lt;br /&gt;
&lt;br /&gt;
'''Raw SQL''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT * FROM x2_contacts WHERE id='.$id)-&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$update='';&lt;br /&gt;
foreach($_POST['Contacts'] as $attribute=&amp;gt;$value){&lt;br /&gt;
    $update.=$attribute.&amp;quot;='&amp;quot;.$value.&amp;quot;', &amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if(!empty($update)){&lt;br /&gt;
    $update=substr($update,0,-2);&lt;br /&gt;
    Yii::app()-&amp;gt;db-&amp;gt;createCommand('UPDATE x2_contacts SET '.$updateQuery.' WHERE id='.$id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''CDbCommand'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;select('*')&lt;br /&gt;
    -&amp;gt;from('x2_contacts')&lt;br /&gt;
    -&amp;gt;where('id=:id', array(':id' =&amp;gt; $id))&lt;br /&gt;
    -&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;update('x2_contacts', $_POST['Contacts'], 'id=:id', array(':id' =&amp;gt; $id));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Model'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$model = X2Model::model('Contacts')-&amp;gt;findByPk($id);&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
$model-&amp;gt;save();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
As can be seen, the Model method is by far the simplest. What's more, none of the 3 listed methods above will actually work with the X2CRM codebase. This is because the way data is stored is not always the same as it is entered by or displayed to a user. For example, date fields display in a nice, localized format and are entered via a Date Picker widget. However, they are stored in the database as UNIX Timestamps for ease of conversion to other formats and mathematical operations. So simply setting &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
will not work, but X2Model has a special method written to handle cases like this. If we instead call&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;setX2Fields($_POST['Contacts']);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The Model class itself will properly format user input data into a database-friendly format. This is because information on each Model attribute is stored in the x2_fields table and can be easily accessed within the Model class. Performing a similar operation via CDbCommand or Raw SQL would be very lengthy and tedious. Models and X2Model in particular really deserve their own page, so an in-depth discussion on that will be left for elsewhere.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Ease of use. Models are incredibly easy to work with and require little to no SQL knowledge to use.&lt;br /&gt;
* Functionality. Models have a very large number of built in functions to make your life easier, and we've made even more of these available through X2Model and its related behaviors.&lt;br /&gt;
* Flexibility. Yii's code has an absolute ton of functionality designed to work with Models. Many, many things are available to load models, get lists of them and render those lists, etc. &lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Size. Models involve loading all of the data for a row from the database table, but have a lot of associated overhead. There are a large number of method calls and pieces of other information stored in the model that can increase overhead.&lt;br /&gt;
* Limited Purpose. Models are mostly intended to be a representation of a row of data, which is fantastic if that's what you need (and for most of what X2 does, it is) but for complicated or specific queries (especially ones collecting aggregate data or statistics) they are not particularly useful.&lt;br /&gt;
* Learning Curve. There are a lot of things Models can do, and it certainly took me a while to figure out a lot of the more interesting or obscure features and methods.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1012</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1012"/>
				<updated>2013-08-15T22:56:06Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
[[yii:CDbCommand|CDbCommand]] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Security. CDbCommand allows for parameter binding, which prevents SQL injection so long as you set up your command properly.&lt;br /&gt;
* Speed. Still fairly quick, as CDbCommand simply converts the command object into a string SQL query.&lt;br /&gt;
* Some complex queries like joins are easier to do with CDbCommand than Raw SQL.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Some simple queries like basic selects are more complicated than Raw SQL.&lt;br /&gt;
* Still requires some SQL knowledge, as most of the command methods are just aliases for SQL commands.&lt;br /&gt;
* Most of Yii's Data Providers / data display objects aren't inherently compatible with CDbCommands (though as of 1.1.13 CSqlDataProvider can take a CDbCommand).&lt;br /&gt;
&lt;br /&gt;
In general, I prefer to use CDbCommand only when there is no Model for the table I am working with, or if I only need 1-2 columns of data and loading the full model would be a waste of memory. It has its uses in some very specific situations but Models should usually be used in favor of this.&lt;br /&gt;
== Models ==&lt;br /&gt;
Models are the primary method of interaction with the database in X2CRM. All Models in the software extend one of two classes: [[x2doc:X2Model|X2Model]] or [[yii:CActiveRecord|CActiveRecord]]. X2Model is a special model class that extends CActiveRecord and is used for Models which have dynamic fields rather than a fixed set of attributes. Examples of this include Contacts, Accounts, and most of the major modules. CActiveRecord models are for tables with static field definitions and don't need the advanced functionality of X2Model. Examples of this include models like Fields, Profiles, and most non-major module related models. &lt;br /&gt;
&lt;br /&gt;
Models provide the easiest method of interaction with database records. For example, if we wanted to load a model into a form, allow a user to edit the data, and then update the database record, here is how it would be done in each of the three methods:&lt;br /&gt;
&lt;br /&gt;
'''Raw SQL''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT * FROM x2_contacts WHERE id='.$id)-&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$update='';&lt;br /&gt;
foreach($_POST['Contacts'] as $attribute=&amp;gt;$value){&lt;br /&gt;
    $update.=$attribute.&amp;quot;='&amp;quot;.$value.&amp;quot;', &amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if(!empty($update)){&lt;br /&gt;
    $update=substr($update,0,-2);&lt;br /&gt;
    Yii::app()-&amp;gt;db-&amp;gt;createCommand('UPDATE x2_contacts SET '.$updateQuery.' WHERE id='.$id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''CDbCommand'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;select('*')&lt;br /&gt;
    -&amp;gt;from('x2_contacts')&lt;br /&gt;
    -&amp;gt;where('id=:id', array(':id' =&amp;gt; $id))&lt;br /&gt;
    -&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;update('x2_contacts', $_POST['Contacts'], 'id=:id', array(':id' =&amp;gt; $id));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Model'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$model = X2Model::model('Contacts')-&amp;gt;findByPk($id);&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
$model-&amp;gt;save();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
As can be seen, the Model method is by far the simplest. What's more, none of the 3 listed methods above will actually work with the X2CRM codebase. This is because the way data is stored is not always the same as it is entered by or displayed to a user. For example, date fields display in a nice, localized format and are entered via a Date Picker widget. However, they are stored in the database as UNIX Timestamps for ease of conversion to other formats and mathematical operations. So simply setting &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
will not work, but X2Model has a special method written to handle cases like this. If we instead call&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;setX2Fields($_POST['Contacts']);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The Model class itself will properly format user input data into a database-friendly format. This is because information on each Model attribute is stored in the x2_fields table and can be easily accessed within the Model class. Performing a similar operation via CDbCommand or Raw SQL would be very lengthy and tedious. Models and X2Model in particular really deserve their own page, so an in-depth discussion on that will be left for elsewhere.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Ease of use. Models are incredibly easy to work with and require little to no SQL knowledge to use.&lt;br /&gt;
* Functionality. Models have a very large number of built in functions to make your life easier, and we've made even more of these available through X2Model and its related behaviors.&lt;br /&gt;
* Flexibility. Yii's code has an absolute ton of functionality designed to work with Models. Many, many things are available to load models, get lists of them and render those lists, etc. &lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Size. Models involve loading all of the data for a row from the database table, but have a lot of associated overhead. There are a large number of method calls and pieces of other information stored in the model that can increase overhead.&lt;br /&gt;
* Limited Purpose. Models are mostly intended to be a representation of a row of data, which is fantastic if that's what you need (and for most of what X2 does, it is) but for complicated or specific queries (especially ones collecting aggregate data or statistics) they are not particularly useful.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1011</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1011"/>
				<updated>2013-08-15T22:02:21Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
[[yii:CDbCommand|CDbCommand]] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Security. CDbCommand allows for parameter binding, which prevents SQL injection so long as you set up your command properly.&lt;br /&gt;
* Speed. Still fairly quick, as CDbCommand simply converts the command object into a string SQL query.&lt;br /&gt;
* Some complex queries like joins are easier to do with CDbCommand than Raw SQL.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Some simple queries like basic selects are more complicated than Raw SQL.&lt;br /&gt;
* Still requires some SQL knowledge, as most of the command methods are just aliases for SQL commands.&lt;br /&gt;
* Most of Yii's Data Providers / data display objects aren't inherently compatible with CDbCommands (though as of 1.1.13 CSqlDataProvider can take a CDbCommand).&lt;br /&gt;
&lt;br /&gt;
In general, I prefer to use CDbCommand only when there is no Model for the table I am working with, or if I only need 1-2 columns of data and loading the full model would be a waste of memory. It has its uses in some very specific situations but Models should usually be used in favor of this.&lt;br /&gt;
== Models ==&lt;br /&gt;
Models are the primary method of interaction with the database in X2CRM. All Models in the software extend one of two classes: [[x2doc:X2Model|X2Model]] or [[yii:CActiveRecord|CActiveRecord]]. X2Model is a special model class that extends CActiveRecord and is used for Models which have dynamic fields rather than a fixed set of attributes. Examples of this include Contacts, Accounts, and most of the major modules. CActiveRecord models are for tables with static field definitions and don't need the advanced functionality of X2Model. Examples of this include models like Fields, Profiles, and most non-major module related models. &lt;br /&gt;
&lt;br /&gt;
Models provide the easiest method of interaction with database records. For example, if we wanted to load a model into a form, allow a user to edit the data, and then update the database record, here is how it would be done in each of the three methods:&lt;br /&gt;
&lt;br /&gt;
'''Raw SQL''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT * FROM x2_contacts WHERE id='.$id)-&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$update='';&lt;br /&gt;
foreach($_POST['Contacts'] as $attribute=&amp;gt;$value){&lt;br /&gt;
    $update.=$attribute.&amp;quot;='&amp;quot;.$value.&amp;quot;', &amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if(!empty($update)){&lt;br /&gt;
    $update=substr($update,0,-2);&lt;br /&gt;
    Yii::app()-&amp;gt;db-&amp;gt;createCommand('UPDATE x2_contacts SET '.$updateQuery.' WHERE id='.$id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''CDbCommand'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;select('*')&lt;br /&gt;
    -&amp;gt;from('x2_contacts')&lt;br /&gt;
    -&amp;gt;where('id=:id', array(':id' =&amp;gt; $id))&lt;br /&gt;
    -&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;update('x2_contacts', $_POST['Contacts'], 'id=:id', array(':id' =&amp;gt; $id));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Model'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$model = X2Model::model('Contacts')-&amp;gt;findByPk($id);&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
$model-&amp;gt;save();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
As can be seen, the Model method is by far the simplest. What's more, none of the 3 listed methods above will actually work with the X2CRM codebase. This is because the way data is stored is not always the same as it is entered by or displayed to a user. For example, date fields display in a nice, localized format and are entered via a Date Picker widget. However, they are stored in the database as UNIX Timestamps for ease of conversion to other formats and mathematical operations. So simply setting &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;attributes = $_POST['Contacts'];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
will not work, but X2Model has a special method written to handle cases like this. If we instead call&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$model-&amp;gt;setX2Fields($_POST['Contacts']);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The Model class itself will properly format user input data into a database-friendly format. This is because information on each Model attribute is stored in the x2_fields table and can be easily accessed within the Model class. Performing a similar operation via CDbCommand or Raw SQL would be very lengthy and tedious. Models and X2Model in particular really deserve their own page, so an in-depth discussion on that will be left for elsewhere.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Ease of use. Models are incredibly easy to work with and require little to no SQL knowledge to use.&lt;br /&gt;
* Functionality. Models have a very large number of built in functions to make your life easier, and we've made even more of these available through X2Model and its related behaviors.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1010</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1010"/>
				<updated>2013-08-14T21:32:21Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
[[yii:CDbCommand|CDbCommand]] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Security. CDbCommand allows for parameter binding, which prevents SQL injection so long as you set up your command properly.&lt;br /&gt;
* Speed. Still fairly quick, as CDbCommand simply converts the command object into a string SQL query.&lt;br /&gt;
* Some complex queries like joins are easier to do with CDbCommand than Raw SQL.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Some simple queries like basic selects are more complicated than Raw SQL.&lt;br /&gt;
* Still requires some SQL knowledge, as most of the command methods are just aliases for SQL commands.&lt;br /&gt;
* Most of Yii's Data Providers / data display objects aren't inherently compatible with CDbCommands (though as of 1.1.13 CSqlDataProvider can take a CDbCommand).&lt;br /&gt;
&lt;br /&gt;
In general, I prefer to use CDbCommand only when there is no Model for the table I am working with, or if I only need 1-2 columns of data and loading the full model would be a waste of memory. It has its uses in some very specific situations but Models should usually be used in favor of this.&lt;br /&gt;
== Models ==&lt;br /&gt;
Models are the primary method of interaction with the database in X2CRM. All Models in the software extend one of two classes: [[x2doc:X2Model|X2Model]] or [[yii:CActiveRecord|CActiveRecord]]. X2Model is a special model class that extends CActiveRecord and is used for Models which have dynamic fields rather than a fixed set of attributes. Examples of this include Contacts, Accounts, and most of the major modules. CActiveRecord models are for tables with static field definitions and don't need the advanced functionality of X2Model. Examples of this include models like Fields, Profiles, and most non-major module related models. &lt;br /&gt;
&lt;br /&gt;
Models provide the easiest method of interaction with database records. For example, if we wanted to load a model into a form, allow a user to edit the data, and then update the database record, here is how it would be done in each of the three methods:&lt;br /&gt;
&lt;br /&gt;
'''Raw SQL''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT * FROM x2_contacts WHERE id='.$id)-&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
$update='';&lt;br /&gt;
foreach($_POST['Contacts'] as $attribute=&amp;gt;$value){&lt;br /&gt;
    $update.=$attribute.&amp;quot;='&amp;quot;.$value.&amp;quot;', &amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if(!empty($update)){&lt;br /&gt;
    $update=substr($update,0,-2);&lt;br /&gt;
    Yii::app()-&amp;gt;db-&amp;gt;createCommand('UPDATE x2_contacts SET '.$updateQuery.' WHERE id='.$id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''CDbCommand'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;select('*')&lt;br /&gt;
    -&amp;gt;from('x2_contacts')&lt;br /&gt;
    -&amp;gt;where('id=:id',array(':id'=&amp;gt;$id))&lt;br /&gt;
    -&amp;gt;queryRow();&lt;br /&gt;
// Code to pass $attributes into the form and load new attributes into $_POST['Contacts']&lt;br /&gt;
Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
    -&amp;gt;update('x2_contacts',$_POST['Contacts'],'id=:id',array(':id'=&amp;gt;$id));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Model'''&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$model=X2Model::model&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1009</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1009"/>
				<updated>2013-08-14T20:57:05Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
[[yii:CDbCommand|CDbCommand]] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Security. CDbCommand allows for parameter binding, which prevents SQL injection so long as you set up your command properly.&lt;br /&gt;
* Speed. Still fairly quick, as CDbCommand simply converts the command object into a string SQL query.&lt;br /&gt;
* Some complex queries like joins are easier to do with CDbCommand than Raw SQL.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Some simple queries like basic selects are more complicated than Raw SQL.&lt;br /&gt;
* Still requires some SQL knowledge, as most of the command methods are just aliases for SQL commands.&lt;br /&gt;
* Most of Yii's Data Providers / data display objects aren't inherently compatible with CDbCommands (though as of 1.1.13 CSqlDataProvider can take a CDbCommand).&lt;br /&gt;
&lt;br /&gt;
In general, I prefer to use CDbCommand only when there is no Model for the table I am working with, or if I only need 1-2 columns of data and loading the full model would be a waste of memory. It has its uses in some very specific situations but Models should usually be used in favor of this.&lt;br /&gt;
== Models ==&lt;br /&gt;
Models are the primary method of interaction with the database in X2CRM. All Models in the software extend one of two classes: [[x2doc:X2Model|X2Model]] or [[yii:CActiveRecord|CActiveRecord]]. X2Model is a special model class that extends CActiveRecord and is used for Models which have dynamic fields rather than a fixed set of attributes. Examples of this include Contacts, Accounts, and most of the major modules. CActiveRecord models are for tables with static field definitions and don't need the advanced functionality of X2Model. Examples of this include models like Fields, Profiles, and most non-major module related models. &lt;br /&gt;
&lt;br /&gt;
Models provide the easiest method of interaction with database records. For example, if we wanted to load a model into a form, allow a user to edit the data, and then update the database record, here is how it would be done in each of the three methods:&lt;br /&gt;
&lt;br /&gt;
Raw SQL &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Code to get the database ID&lt;br /&gt;
$attributes=Yii::app()-&amp;gt;db-&amp;gt;createCommand('SELECT * FROM x2_contacts WHERE id='.$id)-&amp;gt;queryRow;&lt;br /&gt;
// Code to pass $attributes into the form.&lt;br /&gt;
// Code to load the new $attributes into $_POST['Contacts']&lt;br /&gt;
$update='';&lt;br /&gt;
foreach($_POST['Contacts'] as $attribute=&amp;gt;$value){&lt;br /&gt;
    $update.=$attribute.&amp;quot;='&amp;quot;.$value.&amp;quot;', &amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
if(!empty($update)){&lt;br /&gt;
$update=substr($update,0,-2);&lt;br /&gt;
Yii::app()-&amp;gt;db-&amp;gt;createCommand('UPDATE x2_contacts SET '.$updateQuery.' WHERE id='.$id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CDbCommand&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1008</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1008"/>
				<updated>2013-08-08T22:57:33Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
= Raw SQL =&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
= CDbCommand =&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= Models =&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1007</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1007"/>
				<updated>2013-08-08T22:56:46Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
= Raw SQL =&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
= CDbCommand =&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= Models =&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1006</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1006"/>
				<updated>2013-08-08T22:55:37Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
{TODO: Finish CDbCommand, Models,How To's for all sections}&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
= Raw SQL =&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
= CDbCommand =&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1005</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1005"/>
				<updated>2013-08-08T22:55:12Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
= Introduction =&lt;br /&gt;
[[Category:Development]]&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
= Raw SQL =&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
= CDbCommand =&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1004</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1004"/>
				<updated>2013-08-08T22:54:54Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: CDbCommand, Models, How To's for all sections.}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
= Raw SQL =&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
= CDbCommand =&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1003</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1003"/>
				<updated>2013-08-08T22:53:44Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: CDbCommand, Models, How To's for all sections.}&lt;br /&gt;
&lt;br /&gt;
= Core Structure =&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
= Raw SQL =&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
= CDbCommand =&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1002</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1002"/>
				<updated>2013-08-08T22:53:11Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Development]]&lt;br /&gt;
{TODO: CDbCommand, Models, How To's for all sections.}&lt;br /&gt;
&lt;br /&gt;
== Core Structure ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1001</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1001"/>
				<updated>2013-08-08T22:37:22Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{TODO: CDbCommand, Models, How To's for all sections.}&lt;br /&gt;
&lt;br /&gt;
== Core Structure ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the software solely with Raw SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
CDbCommand [http://www.yiiframework.com/doc/api/1.1/CDbCommand] is a class provided by the Yii Framework which allows for one layer of abstraction from writing raw SQL to run queries. As seen in the documentation, wrapper methods for most SQL commands are available so that a query which may look like: &amp;lt;tt&amp;gt;SELECT * FROM x2_contacts WHERE name='John Smith'&amp;lt;/tt&amp;gt; turns into:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select('*')&lt;br /&gt;
-&amp;gt;from('x2_contacts')&lt;br /&gt;
-&amp;gt;where('name=:name', array(':name' =&amp;gt; 'John Smith'))&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This looks more complicated at first glance, and it is slightly--but it doesn't involve writing any SQL. On top of that, some of the more complicated queries get simpler with this class, and also queries with dynamic information are easier. Let's say I wanted to grab every column except &amp;quot;createDate&amp;quot; from an arbitrary model we don't know the type of. First off, this isn't possible with raw SQL. You need to do pre-processing in PHP to get the query correct. You'd have to loop through all of the attributes, build the select statement, and then lookup the table name and pass that into the string. But for a CDbCommand:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$attributes = $model-&amp;gt;attributes;&lt;br /&gt;
unset($attributes['createDate']);&lt;br /&gt;
$attributes = array_keys($attributes);&lt;br /&gt;
$result = Yii::app()-&amp;gt;db-&amp;gt;createCommand()&lt;br /&gt;
-&amp;gt;select($attributes)&lt;br /&gt;
-&amp;gt;from($model-&amp;gt;tableName())&lt;br /&gt;
-&amp;gt;queryAll();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1000</id>
		<title>Interacting with the Database</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Interacting_with_the_Database&amp;diff=1000"/>
				<updated>2013-08-06T22:56:17Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: Created page with &amp;quot;== Core Structure ==  X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Core Structure ==&lt;br /&gt;
&lt;br /&gt;
X2CRM uses MySQL for its database architecture. All tables are prefixed with 'x2_' to differentiate them from any other tables in the database and allow for users to install X2CRM without having to create a database specifically for it. Nearly all tables have an 'id' column which serves as the primary key. Relational data is handled through the x2_relationships table which allows for the specification of model types and IDs rather than through join tables. Tables which are used to store records or other user data generally have associated Model classes to allow for easier data manipulation, while some of the more technical and system data focused tables often have no model record and must be accessed/modified in other ways. There are three ways for a developer to access data stored in one of the tables.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
This is the most simplistic and tedious way to access data. Requires knowledge of the SQL programming language, but allows for the most flexibility and power.&lt;br /&gt;
&lt;br /&gt;
'''Pros'''&lt;br /&gt;
* Speed. Using raw SQL is often the fastest way to retrieve data from the database, there's none of the overhead associated with the other methods.&lt;br /&gt;
* Flexibility. There are several places in the software which generate a large SQL query using a variety of conditionals to dynamically attach and remove pieces of the query. This sort of flexibility is much harder to attain with the other methods.&lt;br /&gt;
* Power. The amount of data and the kind of data you can extract with raw SQL is often much greater than the other methods. There are a variety of MySQL functions which exist that don't really translate well into PHP wrappers.&lt;br /&gt;
'''Cons'''&lt;br /&gt;
* Difficulty. SQL is notoriously difficult to work with due to poorly documented error messages and very little feedback as to if a query is returning the proper data.&lt;br /&gt;
* Messiness. Writing the whole software in SQL would make the codebase look like a mess. SQL queries tend to become lengthy and verbose, and many of the wrapper functions available make the code much cleaner.&lt;br /&gt;
* Security. Improperly used SQL can create vulnerabilities in the software. Raw SQL should be avoided for user entered data if possible to prevent SQL injection. The other methods have easier ways of handling this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Personally I try to use this only when I need something that can't easily be accessed via CDbCommands or Models, or when I need to write a query that will have lots of dynamic parts and doesn't really fit well as a CDbCriteria.&lt;br /&gt;
&lt;br /&gt;
== CDbCommand ==&lt;br /&gt;
{TODO: CDbCommand, Models, How To's for all sections.}&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Google_Integration&amp;diff=999</id>
		<title>Google Integration</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Google_Integration&amp;diff=999"/>
				<updated>2013-07-24T20:05:50Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
Enabling Google Integration will let users into X2CRM using their Google accounts. Furthermore, they will be able synchronize their Google Calendars with content from their X2CRM user calendars, as well as access their Google Drive files.&lt;br /&gt;
&lt;br /&gt;
= Creating an API Project =&lt;br /&gt;
To enable Google integration, you will first need to create a Google API project for your X2CRM installation. To do this:&lt;br /&gt;
# Go to your [https://code.google.com/apis/console/ Google APIs Console] while logged into your Google account.&lt;br /&gt;
# Click &amp;quot;Create Project&amp;quot;. You should be directed to the ''Services'' tab.&lt;br /&gt;
# Turn on the &amp;quot;Calendar&amp;quot;, &amp;quot;Drive&amp;quot;, and &amp;quot;Google+&amp;quot; items by clicking on the toggle switch button next to their names and agreeing to the usage terms and conditions..&lt;br /&gt;
# Go to the ''API Access'' tab and click &amp;quot;Create an OAuth 2.0 client ID&amp;quot;. &lt;br /&gt;
# Enter a product name. This is what users will see when they are prompted to allow the application to access their Google account.&lt;br /&gt;
# For the type of the application, select &amp;quot;Web Application.&amp;quot;&lt;br /&gt;
# You should see a section entitled &amp;quot;Client ID for Web Applications&amp;quot;. In that section, select &amp;quot;Edit Settings.&amp;quot;&lt;br /&gt;
# In the ''Authorized Redirect URIs'' box, put in the following three URIs, replacing &amp;lt;tt&amp;gt;domain.name/webroot&amp;lt;/tt&amp;gt; with the domain name and web directory where X2CRM is installed:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/calendar/syncActionsToGoogleCalendar&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/site/googleLogin&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/site/upload&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
# Furthermore, in the ''Authorized Javascript Origins'' put in the following URI, replacing  &amp;lt;tt&amp;gt;domain.name&amp;lt;/tt&amp;gt; with the domain name where X2CRM is installed:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuring X2CRM =&lt;br /&gt;
# In X2CRM, go to &amp;quot;Google Integration&amp;quot; under the '''System Settings''' section of the '''Admin''' page.&lt;br /&gt;
# Check the box to enable integration, and paste the Client ID and Client Secret from the &amp;quot;Client ID for Web Applications&amp;quot; on the API Access page of your Google Console.&lt;br /&gt;
&lt;br /&gt;
= Individual User Login =&lt;br /&gt;
Each user wishing to log in using their Google account should first go to '''Update Profile''', which can be found in the left menu when they are viewing their profile, and set the '''Google ID''' field to their GMail address. Then, to login:&lt;br /&gt;
# Log out&lt;br /&gt;
# Click &amp;quot;Login with Google&amp;quot; at the login screen, and follow the link &amp;quot;Login with Google ID&amp;quot;&lt;br /&gt;
# Click the necessary button button to permit X2CRM to access your Google account.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Google_Integration&amp;diff=995</id>
		<title>Google Integration</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Google_Integration&amp;diff=995"/>
				<updated>2013-07-10T21:24:22Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
Enabling Google Integration will let users into X2CRM using their Google accounts. Furthermore, they will be able synchronize their Google Calendars with content from their X2CRM user calendars, as well as access their Google Drive files.&lt;br /&gt;
&lt;br /&gt;
= Creating an API Project =&lt;br /&gt;
To enable Google integration, you will first need to create a Google API project for your X2CRM installation. To do this:&lt;br /&gt;
# Go to your [https://code.google.com/apis/console/ Google APIs Console] while logged into your Google account.&lt;br /&gt;
# Click &amp;quot;Create Project&amp;quot;. You should be directed to the ''Services'' tab.&lt;br /&gt;
# Turn on the &amp;quot;Calendar&amp;quot;, &amp;quot;Drive&amp;quot;, and &amp;quot;Google+&amp;quot; items by clicking on the toggle switch button next to their names and agreeing to the usage terms and conditions..&lt;br /&gt;
# Go to the ''API Access'' tab and click &amp;quot;Create an OAuth 2.0 client ID&amp;quot;. &lt;br /&gt;
# Enter a product name. This is what users will see when they are prompted to allow the application to access their Google account.&lt;br /&gt;
# For the type of the application, select &amp;quot;Web Application.&amp;quot;&lt;br /&gt;
# You should see a section entitled &amp;quot;Client ID for Web Applications&amp;quot;. In that section, select &amp;quot;Edit Settings.&amp;quot;&lt;br /&gt;
# In the ''Authorized Redirect URIs'' box, put in the following two URIs, replacing &amp;lt;tt&amp;gt;domain.name/webroot&amp;lt;/tt&amp;gt; with the domain name and web directory where X2CRM is installed:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/calendar/syncActionsToGoogleCalendar&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/site/googleLogin&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/site/upload&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
# Furthermore, in the ''Authorized Javascript Origins'' put in the following URI, replacing  &amp;lt;tt&amp;gt;domain.name&amp;lt;/tt&amp;gt; with the domain name where X2CRM is installed:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuring X2CRM =&lt;br /&gt;
# In X2CRM, go to &amp;quot;Google Integration&amp;quot; under the '''System Settings''' section of the '''Admin''' page.&lt;br /&gt;
# Check the box to enable integration, and paste the Client ID and Client Secret from the &amp;quot;Client ID for Web Applications&amp;quot; on the API Access page of your Google Console.&lt;br /&gt;
&lt;br /&gt;
= Individual User Login =&lt;br /&gt;
Each user wishing to log in using their Google account should first go to '''Update Profile''', which can be found in the left menu when they are viewing their profile, and set the '''Google ID''' field to their GMail address. Then, to login:&lt;br /&gt;
# Log out&lt;br /&gt;
# Click &amp;quot;Login with Google&amp;quot; at the login screen, and follow the link &amp;quot;Login with Google ID&amp;quot;&lt;br /&gt;
# Click the necessary button button to permit X2CRM to access your Google account.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	<entry>
		<id>http://wiki.x2crm.com/index.php?title=Google_Integration&amp;diff=994</id>
		<title>Google Integration</title>
		<link rel="alternate" type="text/html" href="http://wiki.x2crm.com/index.php?title=Google_Integration&amp;diff=994"/>
				<updated>2013-07-10T18:46:08Z</updated>
		
		<summary type="html">&lt;p&gt;Jake: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Support]]&lt;br /&gt;
Enabling Google Integration will let users into X2CRM using their Google accounts. Furthermore, they will be able synchronize their Google Calendars with content from their X2CRM user calendars, as well as access their Google Drive files.&lt;br /&gt;
&lt;br /&gt;
= Creating an API Project =&lt;br /&gt;
To enable Google integration, you will first need to create a Google API project for your X2CRM installation. To do this:&lt;br /&gt;
# Go to your [https://code.google.com/apis/console/ Google APIs Console] while logged into your Google account.&lt;br /&gt;
# Click &amp;quot;Create Project&amp;quot;. You should be directed to the ''Services'' tab.&lt;br /&gt;
# Turn on the &amp;quot;Calendar&amp;quot; item by clicking on the toggle switch button next to its name and agreeing to the usage terms and conditions of the Calendar API.&lt;br /&gt;
# Go to the ''API Access'' tab and click &amp;quot;Create an OAuth 2.0 client ID&amp;quot;. &lt;br /&gt;
# Enter a product name. This is what users will see when they are prompted to allow the application to access their Google account.&lt;br /&gt;
# For the type of the application, select &amp;quot;Web Application.&amp;quot;&lt;br /&gt;
# You should see a section entitled &amp;quot;Client ID for Web Applications&amp;quot;. In that section, select &amp;quot;Edit Settings.&amp;quot;&lt;br /&gt;
# In the ''Authorized Redirect URIs'' box, put in the following two URIs, replacing &amp;lt;tt&amp;gt;domain.name/webroot&amp;lt;/tt&amp;gt; with the domain name and web directory where X2CRM is installed:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/calendar/syncActionsToGoogleCalendar&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/site/googleLogin&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/webroot/index.php/site/upload&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
# Furthermore, in the ''Authorized Javascript Origins'' put in the following URI, replacing  &amp;lt;tt&amp;gt;domain.name&amp;lt;/tt&amp;gt; with the domain name where X2CRM is installed:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://domain.name/&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Configuring X2CRM =&lt;br /&gt;
# In X2CRM, go to &amp;quot;Google Integration&amp;quot; under the '''System Settings''' section of the '''Admin''' page.&lt;br /&gt;
# Check the box to enable integration, and paste the Client ID and Client Secret from the &amp;quot;Client ID for Web Applications&amp;quot; on the API Access page of your Google Console.&lt;br /&gt;
&lt;br /&gt;
= Individual User Login =&lt;br /&gt;
Each user wishing to log in using their Google account should first go to '''Update Profile''', which can be found in the left menu when they are viewing their profile, and set the '''Google ID''' field to their GMail address. Then, to login:&lt;br /&gt;
# Log out&lt;br /&gt;
# Click &amp;quot;Login with Google&amp;quot; at the login screen, and follow the link &amp;quot;Login with Google ID&amp;quot;&lt;br /&gt;
# Click the necessary button button to permit X2CRM to access your Google account.&lt;/div&gt;</summary>
		<author><name>Jake</name></author>	</entry>

	</feed>