PSR-4 compliance & autoloader

This commit is contained in:
Gilles Crettenand
2015-06-02 01:20:48 +02:00
parent 6690b9fe55
commit ab2092e9c3
16 changed files with 128 additions and 87 deletions

106
Lib/Configuration.php Normal file
View File

@@ -0,0 +1,106 @@
<?php
namespace BSR\Lib;
class Configuration {
private static $instance = null;
/**
* ! WARNING !
*
* Those are default values, if you need to change them for a particular host,
* please just create a file named 'configuration.local.php' in the same directory
* and redefine the values there !
*
* This file must contain an array named $configuration containing the keys you
* want to redefine. Then, those values will be used to replace those in the
* array defined just below.
*
* @var array configuration values
*/
private $values = array(
'db' => array(
'driver' => 'SQL Server Native Client 11.0',
'server' => 'BSR2012\SQLEXPRESS',
'port' => '1433',
'username' => 'alcoda',
'password' => 'alcodaonly',
'name' => 'NetBiblio3',
),
'solr' => array(
'server' => '192.168.1.250',
'port' => '8983',
'username' => '',
'password' => '',
'path' => 'solr/bsr1',
'result_count' => 10,
),
'log' => array(
'file' => 'C:\inetpub\wwwroot\WebService\logs\log.txt',
// The greater the verbosity, the more is displayed
// 0 : no log at all
// 1 : log function calls and errors
// 2 : log parameters and sent data
'verbosity' => 0,
),
'session' => array(
'save_path' => ''
),
'checkfile_url' => 'http://fichiers.bibliothequesonore.ch/checkfile.php?',
'netbiblio_worker_id' => 45,
'www_employee_id' => 45,
'www_library_id' => 2,
);
private $custom_config = 'configuration.local.php';
private function __construct() {
// by default, set the session save path to the default value;
$this->values['session']['save_path'] = session_save_path();
if(file_exists($this->custom_config)) {
require_once($this->custom_config);
if(! isset($configuration) || ! is_array($configuration)) {
throw new \RuntimeException("You custom configuration in '{$this->custom_config}' must be in a variable named '\$configuration' and be an array.");
}
$this->values = array_replace_recursive($this->values, $configuration);
}
}
private function dotNotationAccess($data, $key, $default=null)
{
$keys = explode('.', $key);
foreach ($keys as $k) {
if (!is_array($data)) {
throw new \Exception("Try to access non-array as array, key '$key''");
}
if (!isset($data[$k])) {
return $default;
}
$data = $data[$k];
}
return $data;
}
private function value($name, $default) {
return $this->dotNotationAccess($this->values, $name, $default);
}
/**
* @param $name
* @param mixed $default the default value for your configuration option
* @return mixed return the configuration value if the key is find, the default otherwise
*/
public static function get($name, $default = null) {
if(is_null(self::$instance)) {
self::$instance = new Configuration();
}
return self::$instance->value($name, $default);
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace BSR\Lib\Exception;
class BookNotFoundException extends WebException {
public function __construct($code) {
parent::__construct('BookNotFound', "The book with code $code was not found", -404);
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace BSR\Lib\Exception;
/**
* Exception raised when an invalid attribute name is accessed
*/
class InvalidAttributeException extends \Exception { }

View File

@@ -0,0 +1,19 @@
<?php
namespace BSR\Lib\Exception;
class SqlException extends \Exception
{
private $query;
public function __construct($message = "Sql Error", $query = "")
{
$this->query = $query;
parent::__construct($message, 0);
}
public function getSqlError()
{
return $this->getMessage().' while executing: '.$this->query;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace BSR\Lib\Exception;
class WebException extends \Exception
{
private $excname;
/**
* @param string $name
* @param string $reason
* @param int $code
*/
function __construct($name, $reason, $code)
{
$this->excname = $name;
parent::__construct($reason, $code);
}
public function getName()
{
return $this->excname;
}
}

88
Lib/Search/BookSearch.php Normal file
View File

@@ -0,0 +1,88 @@
<?php
namespace BSR\Lib\Search;
use BSR\Lib\Configuration;
mb_http_output('UTF-8');
class BookSearch
{
/** @var SolrClient */
private $client;
/** @var SolrQuery */
private $query;
private $queryParts = array();
public function __construct()
{
$options = array
(
'hostname' => Configuration::get('solr.server'),
'port' => Configuration::get('solr.port'),
'login' => Configuration::get('solr.username'),
'password' => Configuration::get('solr.password'),
'path' => Configuration::get('solr.path'),
);
$this->client = new SolrClient($options);
$this->query = new SolrQuery();
$this->query->setQuery('*:*');
$this->query->addField('*');
$this->query->addParam('q.op', 'AND');
}
public function addQuery($queryText, $queryField = null, $escape = true)
{
if($escape) {
$queryText = SolrUtils::escapeQueryChars($queryText);
}
if (strlen($queryField) > 0) {
$queryText = "$queryField:\"$queryText\"";
}
$this->queryParts[] = $queryText;
}
public function addSortField($field, $order = SolrQuery::ORDER_DESC)
{
$this->query->addSortField($field, $order);
}
/**
* @param int $start
* @param int $count
* @return array
* @throws WebException
*/
public function getResults($start = 0, $count = 15)
{
if (count($this->queryParts) == 0)
$query = '*:*';
else {
$query = implode(' AND ', $this->queryParts);
}
$this->query->setQuery($query);
$this->query->setStart($start);
$this->query->setRows($count);
try {
$results = $this->client->query($this->query)->getResponse();
} catch(SolrClientException $e) {
throw new WebException ("SolrError", $e->getMessage(), -700);
}
$books = isset($results['response']['docs']) ?
array_map(function($o) { return (array) $o; }, $results['response']['docs']) :
false;
return array(
'count' => $results['response']['numFound'],
'facets' => $results['facet_counts']['facet_fields'],
'books' => $books,
);
}
}

107
Lib/WebService.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
namespace BSR\Lib;
use BSR\Lib\Exception\WebException;
abstract class WebService
{
private $func = null;
private $log = '';
/**
* @param $message
* @param int $verbosity
* @param bool $withTime
*/
public function log($message, $verbosity = 1, $withTime = false) {
if(Configuration::get('log.verbosity') < $verbosity) {
return;
}
if($withTime) {
$message = date("d-m-Y h:m:s").' '.$message;
}
$this->log .= $message."\n";
}
public function Run()
{
$this->log("------------------");
$this->log("Start request", 1, true);
$data = array();
try {
$result = $this->Call();
$data["result"][$this->func] = $result;
} catch (WebException $e) {
$data["error"]["code"] = $e->getCode();
$data["error"]["name"] = $e->getName();
$data["error"]["reason"] = $e->getMessage();
$this->log(sprintf("Failure : [%s] %s", $e->getCode(), $e->getName()));
} catch (\Exception $e) {
$data["unexpected error"]["message"] = $e->getMessage();
$this->log(sprintf("Unexpected exception : %s", $e->getMessage()));
}
$this->Send($data);
$this->log("Request finished", 1, true);
$this->log("------------------\n\n");
if(Configuration::get('log.verbosity') > 0) {
file_put_contents(Configuration::get('log.file'), $this->log, FILE_APPEND | LOCK_EX);
}
}
private function Call()
{
ob_start();
session_save_path(Configuration::get('session.save_path'));
session_start();
$params = empty($_GET) ? $_POST : $_GET;
if (empty($params)) {
throw new WebException ("CallArgument", "arguments error", -1);
}
if (!array_key_exists("func", $params)) {
throw new WebException ("CallArgFunction", "no 'func' specified", -2);
}
$this->func = $params["func"];
unset($params['func']);
if (!is_callable(array($this, $this->func))) {
throw new WebException ("CallFunction", "'func' method not available", -3);
}
$rm = new \ReflectionMethod($this, $this->func);
$nbParams = count($params);
$nbArgsFix = $rm->getNumberOfRequiredParameters();
$nbArgs = $rm->getNumberOfParameters();
/* Check the number of arguments. */
if ($nbParams < $nbArgsFix) {
throw new WebException ("CallArgNumber", "you must provide at least " . $nbArgsFix . " arguments", 4);
}
if ($nbParams > $nbArgs) {
throw new WebException ("CallArgNumber", "you must provide at most " . $nbArgs . " arguments", 4);
}
$this->log("Calling '".$this->func."'");
$this->log("Params: ".print_r($params, true), 2);
return call_user_func_array(array($this, $this->func), $params);
}
private function Send(array $data)
{
ob_clean();
flush();
$this->log("Data: ".print_r($data, true), 2);
echo json_encode($data);
}
}

270
Lib/db/AudioBook.php Normal file
View File

@@ -0,0 +1,270 @@
<?php
namespace BSR\Lib\db;
/**
* AudioBook is mapped on a Notice from NetBiblio
*
* @property string id
* @property string sql_id
* @property string title
* @property string sql_title
* @property string author
* @property string sql_author
* @property string code
* @property string sql_code
* @property string summary
* @property string sql_summary
* @property string editor
* @property string sql_editor
* @property string media
* @property string sql_media
* @property string collection
* @property string sql_collection
* @property string isbn
* @property string sql_isbn
* @property string readBy
* @property string sql_readBy
* @property string cover
* @property string sql_cover
* @property string category
* @property string sql_category
* @property string date
* @property string sql_date
* @property string code3
* @property string sql_code3
* @property string code3Long
* @property string sql_code3Long
* @property string genre
* @property string sql_genre
* @property string genreCode
* @property string sql_genreCode
* @property string coverdisplay
* @property string sql_coverdisplay
* @property string link
* @property string sql_link
* @property string linkTitle
* @property string sql_linkTitle
* @property string typeMedia1
* @property string sql_typeMedia1
*/
class AudioBook extends DbMapping
{
protected $attributeNames = 'id title author code summary editor media collection isbn readBy reader cover category date code3 code3Long genre genreCode coverdisplay link linkTitle mediaType typeMedia1';
public static function find($id) {
return self::findBy('NoticeID', $id);
}
/**
* If $id is an array, return an array of book, if it is only one id, return one book
* Load books details only if not an array.
*
* Beware that this search use the NoticeID, not the NoticeNr, if you need the last one
* use findByCode method instead;
*
* @param string $column
* @param int|array $ids
* @param bool $raw results ?
* @return AudioBook|AudioBook[]|array
*/
public static function findBy($column, $ids, $raw = false)
{
$multiple = true;
if(! is_array($ids)) {
$ids = array($ids);
$multiple = false;
}
$sql = sprintf("SELECT DISTINCT
Notices.[NoticeID] AS id,
Notices.[Title] AS title,
Notices.[Author] AS author,
LTRIM(RTRIM(Notices.[NoticeNr])) AS code,
Fields.[520] AS summary,
REPLACE(Fields.[260b], ',', '') AS editor,
LEFT(SUBSTRING(Fields.[260c], PATINDEX('%%[0-9]%%', Fields.[260c]), 8000), PATINDEX('%%[^0-9]%%', SUBSTRING(Fields.[260c], PATINDEX('%%[0-9]%%', Fields.[260c]), 8000) + 'X')-1) AS year,
Fields.[300] AS media,
Fields.[490a] AS collection,
isbn.DisplayText AS isbn,
Fields.[901] AS reader,
Fields.[901] AS readBy, -- for compatibility
Fields.[899a] AS cover,
'' AS category, -- supposed to come from tags 600, 610, 650, 651, 655, 690, 691, 695, 696 but always empty anyway
CONVERT(VARCHAR, Notices.[CreationDate], 102) AS date,
LTRIM(RTRIM(Notices.[userdefined3code])) AS code3,
[Code3].TextFre AS code3Long,
[GenreCode].TextFre AS genre,
[GenreCode].Code AS genreCode,
Notices.[coverdisplay],
Fields.[856u] AS link,
Fields.[856z] AS linkTitle,
Notices.[MediaType1Code] AS mediaType,
Notices.[MediaType1Code] AS typeMedia1 -- for compatibility
FROM Notices
INNER JOIN Codes AS Code3 ON Notices.userdefined3code = Code3.Code AND Code3.Type=6
INNER JOIN Codes AS GenreCode ON MediaType2Code = GenreCode.Code AND GenreCode.Type = 2
LEFT OUTER JOIN (
SELECT *
FROM (
SELECT
NoticeID,
CASE
WHEN Tag IN ('490', '260', '856', '899') THEN Tag + SubfieldCode
ELSE Tag
END AS Field,
CASE
WHEN Tag = '020' THEN CAST(AuthorityID AS varchar)
ELSE ContentShortPart
END AS Data
FROM NoticeFields
WHERE
Tag IN (
'520', -- summary
'901', -- Reader
'300', -- Duration (field media)
'020' -- ISBN
)
OR (Tag = '490' AND SubfieldCode = 'a') -- Collection
OR (Tag = '260' AND SubfieldCode = 'b') -- Editor
OR (Tag = '260' AND SubfieldCode = 'c') -- Edition year
OR (Tag = '856' AND SubfieldCode = 'u') -- Link
OR (Tag = '856' AND SubfieldCode = 'z') -- Link title
OR (Tag = '899' AND SubfieldCode = 'a') -- Cover
) AS src
PIVOT (
MIN(Data)
FOR Field IN ([520], [901], [300], [020], [490a], [260b], [260c], [856u], [856z], [899a])
) AS pvt
) Fields ON Notices.NoticeID = Fields.NoticeID
LEFT JOIN Authorities AS isbn ON isbn.AuthorityID = Fields.[020]
WHERE
LTRIM(RTRIM(Notices.[%s])) IN ('%s')
AND Notices.[NoticeNr] NOT LIKE '%%~%%'
;", $column, implode('\', \'', $ids));
$result = Connection::execute($sql, true);
$books = array();
while($row = $result->next()) {
if($row['coverdisplay'] == 2) {
$row['cover'] = 'http://fichiers.bibliothequesonore.ch:8089/netbiblio/images/covers/Cover'.$row['id'].'_Original.jpg';
} else if(strlen($row['cover']) > 0) {
$row['cover'] = 'http://ecx.images-amazon.com/images/I/'.$row['cover'].'._SL320_.jpg';
}
$books[$row['id']] = $raw ? $row : new AudioBook($row);
}
return $multiple ? $books : reset($books);
}
/**
* Retrieve the list of all readers (volunteers) having read at least 4 books (2 notices per book).
* Returns an associative array containing $lastname and $firstname
*/
public static function listOfReaders()
{
$sql = "SELECT
count(*),
ContentShortPart AS name
FROM NoticeFields
WHERE Tag=901
GROUP BY ContentShortPart
HAVING count(*) > 6
ORDER BY SUBSTRING(ContentShortPart, CHARINDEX(' ', ContentShortPart)+1, 15);";
$results = Connection::execute($sql);
return array_map(function($row) {
$fullname = str_replace("*", "", $row['name']);
$parts = explode(" ", $fullname);
$firstname = array_shift($parts);
$lastname = implode(" ", $parts);
return array(
'lastname' => $lastname,
'firstname' => $firstname);
}, $results->to_array());
}
/**
* Retrieve the list of all genres
*/
public static function ListOfGenres()
{
$sql = "SELECT
LTRIM(RTRIM(Code)) as code,
TextFre AS text
FROM Codes
WHERE
type=2
AND Code!='-'
ORDER BY TextFre;";
$results = Connection::execute($sql);
return array_map(function($row) {
return array(
'code' => $row['code'],
'text' => $row['text'],
);
}, $results->to_array());
}
/**
* Retrieve the list of all type available in the database.
*/
public static function listOfTypes()
{
$sql = "SELECT DISTINCT
Codes.TextFre AS type
FROM Codes
INNER JOIN Notices ON Codes.Code = Notices.MediaType2Code
WHERE
Codes.Type = 2
AND Notices.NoticeNr NOT LIKE '%~%'
AND Notices.NoticeNr NOT LIKE '%V%'
AND Notices.NoticeNr NOT LIKE '%T%'
AND Notices.MediaType1Code = 'CDD';";
$results = Connection::execute($sql);
$results = array_map(function($r) {
return $r['type'];
}, $results->to_array());
array_unshift($results, "Jeunesse");
return $results;
}
/**
* Retrieve the list of all books currently lended to readers.
*/
public static function inReading()
{
$sql = "SELECT
noticenr, title, author, displayName
FROM notices, items, circulations, useraccounts
WHERE
mediatype1code='N' and NoticeNr not like '%~%'
AND items.noticeid = notices.noticeid
AND items.ItemID=circulations.ItemID
AND useraccounts.useraccountid=circulations.useraccountid
ORDER BY author, title;";
$results = Connection::execute($sql);
return array_map(function($row) {
return array(
"noticenr" => $row['noticenr'],
"auteur" => $row['author'],
"titre" => $row['title'],
"lecteur" => $row['displayName']
);
}, $results->to_array());
}
public function __set($name, $value)
{
if ($name == 'code' && is_string($value)) {
$value = preg_replace('/[~a-zA-Z]/', '', $value);
}
parent::__set($name, $value);
}
}

187
Lib/db/Connection.php Normal file
View File

@@ -0,0 +1,187 @@
<?php
namespace BSR\Lib\db;
use BSR\Lib\Configuration;
use BSR\Lib\Exception\SqlException;
class Connection
{
// Internal variable to hold the connection
private static $db;
final private function __construct() {}
/**
* @param $query
* @param bool $throw_error
* @return OdbcResultSet|resource|string
* @throws SqlException
*/
public static function execute($query, $throw_error = false)
{
$result = odbc_exec(self::get(), utf8_decode($query));
$result = new OdbcResultSet($result);
if ($result->is_error()) {
if($throw_error) {
throw new SqlException($result->get_error(), $query);
}
return $result->get_error();
}
return $result;
}
public static function get()
{
if (is_null(self::$db)) {
$dsn = sprintf(
"Driver={%s};Server=%s,%s;Database=%s;",
Configuration::get('db.driver'),
Configuration::get('db.server'),
Configuration::get('db.port'),
Configuration::get('db.name')
);
self::$db = odbc_pconnect($dsn, Configuration::get('db.username'), Configuration::get('db.password'));
if (self::$db === false) {
throw new SqlException("Unable to connect to the server.");
}
}
// Return the connection
return self::$db;
}
final private function __clone() {}
}
class OdbcResultSet implements \Iterator, \ArrayAccess
{
public $length;
private $results;
private $error;
private $num_fields;
private $cursor_index;
public function __construct($odbc_result)
{
if ($odbc_result === false) {
$this->error = odbc_errormsg(Connection::get());
} else {
try {
$this->results = array();
$this->num_fields = odbc_num_fields($odbc_result);
if ($this->num_fields > 0) {
while ($row = odbc_fetch_row($odbc_result)) {
$data = array();
for ($i = 1; $i <= $this->num_fields; ++$i) {
$data[odbc_field_name($odbc_result, $i)] = utf8_encode(odbc_result($odbc_result, $i));
}
$this->results[] = $data;
}
};
} catch (\Exception $e) {
print($e->getMessage());
}
$this->cursor_index = 0;
$this->length = count($this->results);
odbc_free_result($odbc_result);
}
}
public function is_error()
{
return ($this->error ? true : false);
}
public function get_error()
{
return $this->error;
}
public function get_row()
{
return $this->current();
}
public function to_array()
{
return $this->results;
}
// ArrayAccess
/**
* @param int $offset
* @return bool
*/
public function offsetExists($offset)
{
return !$this->error && $this->cursor_index < $this->length && $this->cursor_index >= 0;
}
/**
* @param int $offset
* @return bool|array
*/
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->results[$offset] : false;
}
public function offsetSet($offset, $value)
{
if($this->offsetExists($offset)) {
$this->results[$offset] = $value;
}
}
public function offsetUnset($offset)
{
throw new \RuntimeException("This makes no sense at all.");
}
// Iterator
/**
* @return bool|array
*/
public function current()
{
return $this->offsetGet($this->cursor_index);
}
/**
* @return int
*/
public function key()
{
return $this->cursor_index;
}
/**
* @return array|bool
*/
public function next()
{
$current = $this->current();
++$this->cursor_index;
return $current;
}
public function rewind()
{
$this->cursor_index = 0;
}
/**
* @return bool
*/
public function valid()
{
return $this->offsetExists($this->cursor_index);
}
}

126
Lib/db/DbMapping.php Normal file
View File

@@ -0,0 +1,126 @@
<?php
namespace BSR\Lib\db;
use BSR\Lib\Exception\InvalidAttributeException;
/**
* Base class for mapping objects. inherit you database filled objects from here.
*
* @property int $id
*/
abstract class DbMapping
{
protected $attributes;
protected $attributeNames = '';
protected $privateAttributeNames = '';
/**
* @param array $attributes
*/
public function __construct(array $attributes)
{
$this->setAttributes($attributes);
}
/**
* Define a bunch of attribute given by an associative array
* @param array $attributes
*/
public function setAttributes(array $attributes)
{
$this->assertAttributes($attributes);
foreach ($attributes as $key => $value) {
$this->__set($key, $value);
}
}
/**
* Ensure that all keys from attributes are authorized
* @param array $attributes
*/
private function assertAttributes(array $attributes)
{
foreach ($attributes as $key => $value) {
$this->assertAttribute($key);
}
}
/**
* Ensure that name attribute is authorized
* If public_only is false, check agains PRIVATE_ATTRIBUTES_NAME too.
* Those one cannot be accessed via setAttributes and other batch methods.
* @param string $name
* @param bool $public_only
* @throws InvalidAttributeException if the attribute is not a valid one
*/
private function assertAttribute($name, $public_only = TRUE)
{
if (strpos($this->attributeNames, $name) === false && ($public_only || strpos($this->privateAttributeNames, $name) === false)) {
throw(new InvalidAttributeException("The attribute $name is invalid"));
}
}
/**
* Get a user attribute or the linked whishes
* @param string $name
* @return mixed
*/
public function __get($name)
{
$sql_safe = FALSE;
if (strpos($name, 'sql_') === 0) {
$name = substr($name, 4);
$sql_safe = TRUE;
}
$this->assertAttribute($name, false);
if (isset($this->attributes[$name])) {
$value = $this->attributes[$name];
if ($sql_safe) {
$value = str_replace("'", "''", $value);
}
return $value;
} else {
return NULL;
}
}
public function to_array() {
return $this->attributes;
}
/**
* Set a user attribute
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$this->assertAttribute($name, false);
$this->attributes[$name] = $value;
}
/**
* Function to retrieve data from an id.
* @param int $id
* @return DbMapping
*/
public static function find($id) {
throw new \RuntimeException("This method must be implemented in child classes.");
}
/**
* Return all the public attributes in an array;
*/
public function toArray()
{
$result = array();
foreach ($this->attributes as $name => $value) {
if (strpos($this->attributeNames, $name) !== false) {
$result[$name] = $value;
}
}
return $result;
}
}

209
Lib/db/User.php Normal file
View File

@@ -0,0 +1,209 @@
<?php
namespace BSR\Lib\db;
use BSR\Lib\Configuration;
/**
* User is mapped on the Useraccounts table. Contains user information : id, login, firstName, lastName, displayName.
*
* @property int id
* @property string $login
* @property string $sql_login
* @property string $password
* @property string $sql_password
* @property string $privatePhone
* @property string $sql_privatePhone
* @property string $officePhone
* @property string $sql_officePhone
* @property string $mobilePhone
* @property string $sql_mobilePhone
* @property string $addressId
* @property string $sql_addressId
* @property string $displayName
* @property string $sql_displayName
* @property string $firstName
* @property string $sql_firstName
* @property string $lastName
* @property string $sql_lastName
* @property string $mail
* @property string $sql_mail
*/
class User extends DbMapping
{
public static $tableName = 'Useraccounts';
public static $idColumn = 'UseraccountID';
protected static $addressTableName = 'Addresses';
protected static $addressIdColumn = 'AddressID';
protected static $wishTableName = 'Wishes';
protected $attributeNames = 'id login firstName lastName displayName freeOne mail addressId mobilePhone officePhone privatePhone';
protected $privateAttributeNames = 'password';
/**
* @param string $login Login for the user
* @param string $password Password for the user
* @return User|null User object if we were able to authenticate
*/
public static function authenticate($login, $password)
{
$password = str_replace("'", "''", $password);
return User::find($login, " UPPER(password) = UPPER('$password') ", false);
}
/**
* Retrieve a user by its login. Do not represent a valid authentication.
*
* Cond has to be safe because no check are made inside.
*
* @param string $login login the login name
* @param string $cond a condition to restrict the choice, optional
* @param bool $raiseError
* @return User the User object or NULL if no user found.
*/
public static function find($login, $cond = '', $raiseError = true)
{
$login = str_replace("'", "''", $login);
if(strlen($cond) > 0) {
$cond = " AND $cond";
}
$sql = sprintf("SELECT TOP 1
[FirstName] AS firstName,
[LastName] AS lastName,
[DisplayName] AS displayName,
[UserDefined1] AS freeOne,
[ActualAddressID] AS addressId,
[Email] AS mail,
[TelephoneMobile] AS mobilePhone,
[TelephonePrivate] AS privatePhone,
[Telephone] AS officePhone,
[%s] AS id,
REPLACE(UseraccountNr, ' ', '') AS login
FROM [%s] AS u
LEFT JOIN [%s] AS a ON a.[%s] = u.[ActualAddressID]
WHERE REPLACE(UseraccountNr, ' ', '') = '%s' AND disabled = 1 %s;",
self::$idColumn, self::$tableName, self::$addressTableName, self::$addressIdColumn, $login, $cond);
$results = Connection::execute($sql, $raiseError);
return $results->current() !== false ? new User($results->current()) : null;
}
private function _getCirculations($table, $sort = "ItemNr ASC") {
$sql = sprintf("SELECT
NoticeID,
CheckOutDate,
ItemNr
FROM %s AS c
INNER JOIN Items AS i ON i.ItemId = c.ItemId
WHERE
c.UseraccountId = %s
ORDER BY %s", $table, $this->id, $sort);
$result = Connection::execute($sql);
$circulations = $result->to_array();
$books = array_map(function($c) { return $c['NoticeID']; }, $circulations);
$books = AudioBook::findBy('NoticeID', $books, true);
foreach($circulations as $c) {
$books[$c['NoticeID']]['date'] = $c['CheckOutDate'];
$books[$c['NoticeID']]['itemNr'] = $c['ItemNr'];
}
return $books;
}
public function getCirculations()
{
return $this->_getCirculations('Circulations');
}
public function getOldCirculations()
{
return $this->_getCirculations('OldCirculations', 'CheckOutDate DESC');
}
/**
* Add a book to the wish list if it is not already inside.
* @param string $noticeNr
* @return bool
*/
public function addWish($noticeNr)
{
if ($this->hasWish($noticeNr)) {
return false;
}
$sql = "UPDATE Counters
SET WishID = WishID + 1
OUTPUT INSERTED.WishID;";
$result = Connection::execute($sql, true);
$row = $result->current();
$employee_id = Configuration::get('www_employee_id');
$library_id = Configuration::get('www_library_id');
$sql = sprintf("INSERT INTO %s
(WishID, NoticeID, %s, CreationDate, EmployeeID, BranchOfficeID, Remark, ModificationDate)
SELECT %s , NoticeID, %s, GETDATE() , %s , %s , '' , GETDATE()
FROM Notices
WHERE LTRIM(RTRIM(NoticeNr)) = '%s';",
User::$wishTableName, User::$idColumn, $row['WishID'], $this->id, $employee_id, $library_id, $noticeNr);
Connection::execute($sql);
return true;
}
/**
* Return true if the book is in the wish list
* @param string $noticeNr
* @return bool
*/
private function hasWish($noticeNr)
{
$sql = sprintf("SELECT w.NoticeID
FROM Wishes AS w
INNER JOIN Notices AS n ON n.NoticeID = w.NoticeID
WHERE
LTRIM(RTRIM(n.NoticeNr)) = '%s'
AND w.UseraccountId = %s;", $noticeNr, $this->id);
$result = Connection::execute($sql);
return $result->current() !== false;
}
/**
* Wishes are all the books that this user want to read.
* @param int $limit
* @return AudioBook[]
*/
public function getWishes($limit = 50)
{
$sql = sprintf("SELECT TOP $limit
NoticeID
FROM %s
WHERE %s = %s
ORDER BY CreationDate DESC"
,User::$wishTableName, User::$idColumn, $this->id);
$result = Connection::execute($sql);
$ids = array_map(function($r) { return $r['NoticeID']; }, $result->to_array());
return AudioBook::findBy('NoticeID', $ids, true);
}
/**
* Remove a book from the wish list
* @param string $noticeNr
*/
public function deleteWish($noticeNr)
{
$sql = sprintf("DELETE w
FROM %s AS w
INNER JOIN Notices AS n ON n.NoticeID = w.NoticeID
WHERE
LTRIM(RTRIM(n.NoticeNr)) = '%s'
AND %s = %s;", User::$wishTableName, $noticeNr, User::$idColumn, $this->id);
Connection::execute($sql, true);
}
}