This article is intended to provide fundamental information on basic tasks that will assist in the back-end development of a client site. You will find videos of the various parts of the articles. The full video will be provided at the end of the article. # Working with Pipelines and Hooks### PipelinesA pipeline is used to create new or customize functionality.* Create a folder called `Pipelines`
* SerialPipeline\<> takes 3 arguments.
-  The type of the pipeline itself (your new pipeline).
-  The type of the input.
-  The type of the output.```
public class MyCustomPipeline
: SerialPipeline<MyCustomPipeline, string, ProductModel>
{
}
```* Implementing SerialPipeline\<YourPipeline, string, ProductModel> will give you the method that executes the pipeline, ExecuteDefaultAsync.
* ExecuteDefaultAsync's parameters include:
- The input (string input).
- The context (IPipelineContext context)
- The cancellation token (CancellationToken token)```
public override ValueTask<ProductModel> ExecuteDefaultAsync(
string input,
IPipelineContext context,
CancellationToken token = default)
{
// Execute custom logic here...
}
```> [!info]
> Another type of pipeline is the Parallel Pipeline. Currently, as of <2024-07-18>, there is only one parallel pipeline, that is used to retrieve rate quotes from multiple shipping providers. These pipelines, while less common, currently follow the same convention as above.### HooksA hook is used to extend core functionality. By creating a hook, you will be writing code that will execute after the core pipeline executes.*  Add a new file ({CustomFunctionality}Hook.cs) in the \`Pipelines\` directory.
* The hook you will be creating will derive from {Original Pipeline Name}.Hook```
public class CustomLogicHook : CoreLogicPipeline.Hook
{
}
```* Implement the inherited methods and add custom logic as necessary.[](/wiki-files/files/5025c63a-7274-402e-baf7-23b8a8408706/PHX_Pipelines.mp4)# Creating a new database Entity*  Add a new folder called \`DataModel\`
*  The entities you create will derive from BaseEntity
*  Add the attribute \[SQLTable] and properties to the class that you will create.```
[SQLTable]
public class MyNewEntity : BaseEntity
{
public string StringProperty { get; set; }
public decimal DecimalProperty { get; set; }
}
```> [!info]
> Compiling the code will handle adding the changes to a migration and updating the database.[](/wiki-files/files/999ec9f1-5399-473f-a9f7-ed5e00cca45a/PHX_CreatingNewDBEntities.mp4)# Querying the Database### Creating the database callThere are several different options for accessing the database, but the most common way will be using the IPipelineContext or context. If you have access to the context in your method, the call to the database will look like:```
await using var db = context.GetDisposableDatabaseContext(token);
```> [!info]
> await can be used to run Dispose() on the context asynchronously in async methods. using is not necessary, however you will need to manually run Dispose() on the context.> [!info]
> Currently the cancellation token is not fully functional but should be passed and is considered best practice.The call above returns a db context that only applies to your scope, so will not conflict with any other context calls.### Retrieving data using the contextAs an example, getting products from the database will look like:```
var products = await db.Set<Product>().ToListAsync(token);
```This call can be followed by valid linq to filter entities as necessary.> [!danger]
> Though it may be tempting, avoid using context.GetDatabaseContext(). This call will return a scoped db context, however, will not be scoped to ***your*** current scope. If you are working with parallel pipelines and multiple parallel pipelines that contain this method run in parallel, the db call will throw. If there is a call upstream that has changes and SaveChanges is called by your scope, the changes upstream will also be saved as is.[](/wiki-files/files/83b893de-6c22-40af-8318-40aa4e7efb1b/PHX_QueryingTheDatabase.mp4)# Working with Models### Creating a new model* Create a folder \`Models\`.
* Add the new Model {Name of your new model}Model.cs to the \`Models\` folder.
* The new model should derive from BaseModel
* Add properties as necessary.```
public class MyCustomModel : BaseModel
{
public string CustomProperty { get; set; }
...
}
```> [!warning]
> The interface IModel\<> is available but should generally be avoided. This interface provides read api endpoints for the new model (GetById, GetByKey, List) but does not provide any authentication or authorization. If you are unsure whether your entity should require authentication or authorization, please reach out to project development leadership for guidance. ### Mapping propertiesThe properties will automatically be mapped, provided the property names can be resolved, however there are several ways to employ explicit and even custom mapping on the model.1)  **Mapping by attributes**
The first method is by providing an attribute on the property that will explicitly map the intended entity's property to your own.```
public class MyCustomModel : BaseModel
{
[MapFrom("OriginalPropertyName")]
public string CustomProperty { get; set; }
...
}
```2)  **Mapping by static constructor**
The second method of explicit mapping is by using a static constructor on the new model.```
public class MyCustomModel : BaseModel
{
static MyCustomModel()
{
Mapper<OriginalEntity, MyCustomModel>.SetProperty(
x => x.CustomProperty,
x => x.OriginalPropertyName);
}
public string CustomProperty { get; set; }
...
}
```3)  **Custom mapping with the static constructor**
The third method is used to customize or transform the data that is mapped.```
public class MyCustomModel : BaseModel
{
static MyCustomModel()
{
Mapper<OriginalEntity, MyCustomModel>.SetProperty(
x => x.CustomProperty,
x => x.ProductAssociations.Where(x => x.TypeId == 3).First().Secondary!.CustomKey);
}
public string CustomProperty { get; set; }
...
}
```> [!warning]
> In SetProperty, the first argument must be a simple property selector and the second argument must be valid linq. Methods such as, Contract.CheckValidID() will throw during the execution of the query.  ### Mapping the model from the databaseThe entity can be mapped using the newly created model while using a call to the database.```
await using var db = context.GetDisosableDatabaseContext(token);
var products = await db.Set<Products>()
.Map<MyCustomModel>()
.ToListAsync(token);
```[](/wiki-files/files/b49bbecf-43b0-45e7-8e31-38930411ece4/PHX_ModelsAndMapping.mp4)# Creating client settings### Creating and populating client settings*  Create a new class {Project Acronym}Settings.cs
*  The new settings class must derive from ISettings
*  Add properties to the class (with default values, if desired) to add the settings.```
public class ProjectAcronymSettings : ISettings
{
public string CustomStringSetting { get; set; } = "This is my default";
}
```### Accessing settings from a pipelineIn a pipeline, you can access the settings, and any settings for that matter by using the context to retrieve them from the database.```
var settings = context.GetSettings<MyCustomSettings>();
```> [!info]
> This is also the method to get OOB settings as well.[](/wiki-files/files/93db5f47-777f-4fc4-a001-75c5cba259a9/PHX_Settings.mp4)# Full Video Recording[](</wiki-files/files/1d718531-6fca-4e0a-918f-fe816f05d309/[SUM] PHX Kickstart-20240717_130455-Meeting Recording.mp4>)<br>