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.*.
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"
}
}
❗️ 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.
If you're not using Flex, you should enable the bundle.
$bundles = [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
[...]
new W3com\BoomBundle\W3comBoomBundle(),
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.
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)
{
//...
}
}
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
}
}
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.
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 !
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")');
$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);