Skip to content

w3com-sas/boom

Repository files navigation

W3COM BOOM : Business One Object Manager

This bundle provides a bridge between an SAP HANA database, via its Service Layer and OData Service connections.
Compatible with Symfony 3.3.*, 4.0.* and 4.1.*.

Installation

Download the bundle

Use SATIS to get the bundle through composer by modifying your composer.json :

{
    "repositories": [
        {
            "type": "composer",
            "url": "https://satis.w3cloud.fr"
        }
    ]
}

Also add the dependency :

{
"require": {
        "w3com-sas/boom": "^1.0"
    }
}

Configure the bundle

❗️ Warning ❗️ the config is not the same if you use Symfony Flex. There is no automated Flex recipe since this is a private package.

Not using Flex ? Add the following snippet to the app/config/config.yml file
Using Flex ? Add it to a new config/packages/boom.yaml file.

w3com_boom:
    service_layer:
        verify_https: false
        max_login_attempts: 5
        cookies_storage_path: '%kernel.project_dir%/var/cookies'
        connections: '%sl_connections%'
    odata_service:
        verify_https: false
        connections: '%ods_connections%'
    app_namespace: AppBundle # It's App if you're using Flex

Non-Flex apps : add this to your parameters.yml.dist :

# W3com BOOM
    sl_connections:
        default:
            uri: https://xxxxxx
            path: /
            username: xxxxx
            password: xxxxx
            database: xxxxx
    ods_connections:
        default:
            uri: https://xxxxx
            path: /
            username: xxxxx
            password: xxxxx

Non-Flex apps : adjust your parameters.yml on each machine accordingly. You can define as many Service Layer connections as you want :

sl_connections:
        default:
            uri: https://default_uri
            path: /default_path
            username: default
            password: defaultpass
            database: SBO_DEFAULT
        connection1:
            uri: https://uri1
            path: /path1
            username: user1
            password: user1pass
            database: SBO_DEFAULT
        connection2:
            uri: https://uri2
            path: /path2
            username: user2
            password: user2pass
            database: SBO_OTHER
# Other parameters not shown, refer to your parameters.dist.yml

Flex apps : add the following snippet to the parameters key in the config/services.yaml file. You can define as many Service Layer connections as you want. Here we have two connections, default and prod.

parameters:
    sl_connections:
        default:
            uri: '%env(boom_sl_default_baseuri)%'
            path: '%env(boom_sl_default_path)%'
            username: '%env(boom_sl_default_username)%'
            password: '%env(boom_sl_default_password)%'
            database: '%env(boom_sl_default_database)%'
        prod:
            uri: '%env(boom_sl_prod_baseuri)%'
            path: '%env(boom_sl_prod_path)%'
            username: '%env(boom_sl_prod_username)%'
            password: '%env(boom_sl_prod_password)%'
            database: '%env(boom_sl_prod_database)%'
    ods_connections:
        default:
            uri: '%env(boom_ods_default_baseuri)%'
            path: '%env(boom_ods_default_path)%'
            username: '%env(boom_ods_default_login)%'
            password: '%env(boom_ods_default_password)%'
        prod:
            uri: '%env(boom_ods_prod_baseuri)%'
            path: '%env(boom_ods_prod_path)%'
            username: '%env(boom_ods_prod_login)%'
            password: '%env(boom_ods_prod_password)%'

Flex apps : set your external parameters, see the Symfony doc.
Feel free to rename your parameters, as long as you modify the config/services.yaml file accordingly.

Register the bundle in app/AppKernel.php :

If you're not using Flex, you should enable the bundle.

$bundles = [
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            [...]
            new W3com\BoomBundle\W3comBoomBundle(),

Usage

Writing entities and repositories

Entities

For each table you want to use, you need to write an entity.
❗️ Entities should be placed in AppBundle\HanaEntity and extend the W3com\BoomBundle\HanaEntity\AbstractEntity class.
❗️ You must use annotations to map class fields to table columns, doctrine-like.
❗️ Setters should use the set($field, $value) method from parent class.

Note that the required namespace depends on the Symfony version you're using. Use AppBundle\HanaEntity for Symfony 3.* and App\HanaEntity for Symfony 4.*.

Entities should look like this :

namespace AppBundle\HanaEntity;

use W3com\BoomBundle\Annotation\EntityColumnMeta;
use W3com\BoomBundle\Annotation\EntityMeta;
use W3com\BoomBundle\HanaEntity\AbstractEntity;

/**
 * @EntityMeta(read="ods", write="sl", aliasRead="U_W3C_TABLE", aliasWrite="U_W3C_TABLE")
 */
class MyTable extends AbstractEntity
{
    /**
     * @var int
     * @EntityColumnMeta(column="Code", isKey=true)
     */
    protected $code;

    /**
     * @var int
     * @EntityColumnMeta(column="Name")
     */
    protected $name;

    /**
     * @var string
     * @EntityColumnMeta(column="U_W3C_FIELD", readOnly=true)
     * This column is read-only, Boom will never try to update its value in SAP
     */
    protected $field;
    
    /**
     * @var string
     * @EntityColumnMeta(column="U_W3C_FIELD2", readColumn="Field2")
     * This column will be read with Field2 but updated with U_W3C_FIELD2,
     * it's useful when you read with ODS but write with SL
     */
    protected $field2;

    /**
     * @return int
     */
    public function getCode(): int
    {
        return $this->code;
    }

    /**
     * @param int $code
     * @return MyTable
     */
    public function setCode(int $code)
    {
        return $this->set('code', $code);
    }

    // lots of getters and setters

}

Note that it's not mandatory that your entities are placed just in the HanaEntity folder. You can use any subfolders you want to keep your entities organized.

Custom repositories (optional)

If you need to write some methods that you want to use here and there in your app, you should write repositories for your entities.

❗️ Repos should be placed in AppBundle\HanaRepository and extend the W3com\BoomBundle\HanaRepository\AbstractRepository class.
❗️ If you placed your entities in subfolders (see above), you must respect the exact same organization for your repos.

Again, Use AppBundle\HanaRepository for Symfony 3.* and App\HanaRepository for Symfony 4.*.

namespace AppBundle\HanaRepository;


use W3com\BoomBundle\Repository\AbstractRepository;

class MyTableRepository extends AbstractRepository
{
    // this is an example, write whatever is usefull !
    public function findByToken($token)
    {
        //...
    }
}

Start sending requests

Get the manager

Use Symfony autowiring to retrieve the manager :

use W3com\BoomBundle\Service\BoomManager;

class DefaultController extends Controller
{
    public function indexAction(BoomManager $manager)
    {
        // $manager is now your Boom Manager
    }
}

Get the repo

Now that you have retrieved the manager, use it to get the repo for your entity.
You must type the namespace to your entity, minus the App[Bundle]\HanaRepository part :

$repo = $manager->getRepository('MyTable');
$repo2 = $manager->getRepository('Namespace\Entity');

If you wrote a custom repo, it will be instanciated, and if you didn't, you will get a W3com\BoomBundle\HanaRepository\DefaultRepository object.

Finding objects

You now have access to a few methods to help you find objects.

To find a specific object :

// Boom will know which column he should test the key against. 
// This will return an AbstractEntity object, or null if not exactly one result was returned.
$object = $repo->find($key);

To create a more complex request, use the Parameters class.

use W3com\BoomBundle\Parameters\Clause;
use W3com\BoomBundle\Parameters\Parameters;

$repo = $manager->getRepository('MyTable');
$params = $repo->createParameters()
    ->addFilter('columnName', 'value') // filters on columnName = value
    ->addFilter('columnName', 'value', Clause::GREATER_THAN)
    ->addSelect('columnName') // will only hydrate the requested column, others will be set as null.
    ->addSelect(array('columnName1', 'columnName2))
    ->addOrder('columnName') // will order results on columnName ASC by default
    ->addOrder('columnName', Parameters::ORDER_DESC)
    ->setTop(10);
    
$results = $repo->findAll($params);

❗️ You should be careful with the addSelect method, as you won't be able to distinguish real null values from SAP, and null values from non-requested columns !

Creating filters

You can use one of these clauses with the addFilter method :

  • Clause::EQUALS (which is the default)
  • Clause::NOT_EQUALS
  • Clause::STARTS_WITH
  • Clause::ENDS_WITH
  • Clause::CONTAINS
  • Clause::SUBSTRINGOF
  • Clause::GREATER_THAN
  • Clause::GREATER_OR_EQUAL
  • Clause::LOWER_THAN
  • Clause::LOWER_OR_EQUAL

You can also use Clause::OR and Clause::AND in the last argument when adding a filter, so that this filter is prefixed with a logical operator :

$params
    ->addFilter('columnName','value', Clause::CONTAINS)
    ->addFilter('otherColumn, 'otherValue', Clause::EQUALS, Clause::OR);
// columnName contains value OR otherColumn equals otherValue

If you just want to find multiple values in the same column, pass an array as second argument :

$params
    ->addFilter('columnName',['value1', 'value2', 'value3']);
// columnName equals value1 OR value2 OR value3

You can also search for null values if your Calculation View supports it :

$params
    ->addFilter('columnName', null);
// columnName is null (note: this is NOT an empty string !)

Want to write a custom filter with "and" and "or" mixed, with parenthesis groups ? Use addRawFilter(), but you're on your own on this one :
(note that in this case the filter will be passed as is, so you must use the real column names as they are in SAP B1)

$params
    ->addRawFilter('(U_W3C_FIELD eq "value1" or U_W3C_COL eq "value2") and (U_W3C_ONE eq "value1" and U_W3C_TWO eq "value2")');

Creating, updating or deleting

$object = new MyTable();
$code = $repo->getNextCode();
$object->setCode($code)->setField('myValue')->setOtherField('someValue');
$repo = $manager->getRepository('MyTable');
$repo->add($object);

❗️ For now, the getNextCode() method is just for entities which has their key called code.

$object->setField('myValue')->setOtherField('someValue');
$repo = $manager->getRepository('MyTable');
$repo->update($object);
$key = $object->getCode() // or any method that gets the object key property
$repo = $manager->getRepository('MyTable');
$repo->delete($key);