Hi I'm new to Laravel framework and I'm trying to inject process ID into every log passed to Google Logging service.
I've been able to see the log passed into Google Logging service now, but I've no idea how I could inject more info(Process ID in this case) into my log message.
So far I've tried "tap" method and I can see addition info injected into my log while reading laravel.log file, but same method doesn't seems to work while using Google Cloud Logging plugin.
Below is my script for Google Logging service.
Inside config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['stackdriver'],
],
'stackdriver' => [
'driver' => 'custom',
'via' => App\Logging\CreateStackdriverLogger::class,
'level' => 'debug',
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'tap' => [App\Logging\ProcessorTap::class],
],
];
CreateStackdriverLogger.php
use Google\Cloud\Logging\LoggingClient;
use Monolog\Handler\PsrHandler;
use Monolog\Logger;
class CreateStackdriverLogger
{
/**
* Create a custom Monolog instance.
*
* #param array $config
* #return \Monolog\Logger
*/
public function __invoke(array $config)
{
$logName = isset($config['logName']) ? $config['logName'] : 'app';
$psrLogger = LoggingClient::psrBatchLogger($logName);
$handler = new PsrHandler($psrLogger);
$logger = new Logger($logName, [$handler]);
return $logger;
}
}
Code for 'tap' method, I'm able to see 'pid' inside 'extra', but same method don't work with 'stackdriver'.
ProcessorTab.php
namespace App\Logging;
use Illuminate\Log\Logger;
use Illuminate\Support\Arr;
use Monolog\Formatter\LineFormatter;
class ProcessorTap
{
public function __invoke($logger)
{
foreach ($logger->getHandlers() as $handler) {
$handler->pushProcessor(function ($record) {
return Arr::add($record, 'prefix', getmypid());
});
$handler->setFormatter($this->getLogFormatter());
$handler->pushProcessor([$this, 'processLogRecord']);
}
}
public function processLogRecord(array $record): array
{
$record['extra'] += [
'pid' => getmypid(),
];
return $record;
}
protected function getLogFormatter()
{
$format = "[%datetime%] %channel%.%level_name%: %prefix%.%message% %context% %extra%\n";
return new LineFormatter($format, null, true, true);
}
}
Related
In my laravel application there's need to format a message I need to send into slack. Hence I set a slack log channel into config/logging.php:
'slack' => [
'driver' => 'slack',
'url' => /*Censored Hook URL*/,
'username' => 'MyApp',
'emoji' => ':gear:',
'level' => 'debug',
],
Also as seen on documentation I can do a monolog formater, hence I did the following:
namespace App\Logging;
class SlackLogFormatter
{
/**
* Customize the given logger instance.
*
* #param \Illuminate\Log\Logger $logger
* #return void
*/
public function __invoke($logger)
{
foreach ($logger->getHandlers() as $handler) {
$handler->setFormatter(...);
}
}
}
And specified it as tap into my log:
'slack' => [
'driver' => 'slack',
'tap' => [App\Logging\SlackLogFormatter::class]
'url' => /*Censored Hook URL*/,
'username' => 'MyApp',
'emoji' => ':gear:',
'level' => 'debug',
],
But in my formater where do I process the log entry itself? I mean:
The $handler->setFormatter does not seem to be a method of \Illuminate\Log\Logger class.
I cannot find out what method I need to override when I need to provide a custom format. I mean I have the invoke method then afterwards what?
Dimitrios is almost right (or perhaps this worked on older versions) but as Xavier said, if you copy that code exactly you will get the error
Return value of Monolog\Handler\AbstractProcessingHandler::processRecord() must be of the type array, null returned
You are going to want to do something like this instead :
<?php
namespace App\Logging;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\SlackWebhookHandler;
use Request;
class CustomiseFormatter
{
protected $request;
public function __construct(Request $request = null)
{
$this->request = $request;
}
public function __invoke($logger)
{
foreach ($logger->getHandlers() as $handler) {
if ($handler instanceof SlackWebhookHandler) {
$handler->setFormatter(new LineFormatter(
'[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
));
$handler->pushProcessor([$this, 'processLogRecord']);
}
}
}
public function processLogRecord(array $record): array
{
$record['extra'] += [
'url' => env("APP_URL"),
];
return $record;
}
}
Equally, as with how extra has be modified, you can change any other value.
The formatter for slack should be the following:
namespace App\Logging;
use Monolog\Formatter\LineFormatter;
class SlackLogFormatter
{
/**
* Customize the given logger instance.
*
* #param \Illuminate\Log\Logger $logger
* #return void
*/
public function __invoke($logger)
{
foreach ($logger->getHandlers() as $handler) {
if ($handler instanceof SlackWebhookHandler) {
$format=""; // Look on the Monolog's Line formatter documentation
$formatter= new LineFormatter($format,"Y-m-d H:i:s");
$handler->pushProcessor(function ($record) {
//Append extra info of formatting here
});
$handler->setFormatter($formatter);
}
}
}
}
And config the slack not to send attachment nessesary for the formatter to work:
'slack' => [
'driver' => 'slack',
'tap' => [App\Logging\SlackLogFormatter::class]
'url' => /*Censored Hook URL*/,
'username' => 'MyApp',
'emoji' => ':gear:',
'level' => 'debug',
'attachment' => FALSE,
],
The setFormatter method takes a new Monolog Formatter as seen in: https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#formatters
Also the pushProcessor allows you to populate extra fields on your message eg. Showing an emoji on your log message:
public function __invoke($logger)
{
foreach ($logger->getHandlers() as $handler) {
if ($handler instanceof SlackWebhookHandler) {
$format="%emoji% %message%";
$formatter= new LineFormatter($format,"Y-m-d H:i:s");
$handler->pushProcessor(function ($record) {
$record['emoji']=":poop:";
});
$handler->setFormatter($formatter);
}
}
}
I have a laravel 5.7 application where I want to add prooph for event sourcing. I have follow the instructions but I retrieve this error:
Prooph\ServiceBus\Exception\RuntimeException: CommandBus was not able to identify a CommandHandler for command App\src\Prooph\Model\Procedure\Command\ChangeProcedureState in /var/www/html/vendor/prooph/service-bus/src/CommandBus.php:58
This is my config/prooph.php file
return [
'event_store' => [
'adapter' => [
'type' => \Prooph\EventStore\Pdo\MySqlEventStore::class,
'options' => [
'connection_alias' => 'laravel.connections.pdo',
],
],
'plugins' => [
\Prooph\EventStoreBusBridge\EventPublisher::class,
\Prooph\EventStoreBusBridge\TransactionManager::class,
],
'procedure_collection' => [
'repository_class' => App\src\Prooph\Infrastructure\Repository\EventStoreProcedureCollection::class,
'aggregate_type' => App\src\Prooph\Model\Procedure\Procedure::class,
],
],
'service_bus' => [
'command_bus' => [
'router' => [
'type' => \Prooph\ServiceBus\Plugin\Router\CommandRouter::class,
'routes' => [
// list of commands with corresponding command handler
\App\src\Prooph\Model\Procedure\Command\ChangeProcedureState::class => App\src\Prooph\Model\Procedure\Handler\ChangeProcedureStateHandler::class,
],
],
],
'event_bus' => [
'plugins' => [
\Prooph\ServiceBus\Plugin\InvokeStrategy\OnEventStrategy::class,
],
'router' => [
'routes' => [
// list of events with a list of projectors
],
],
],
/*'event_bus' => [
'router' => [
'routes' => [
// list of events with a list of projectors
App\src\Prooph\ProophessorDo\Model\User\Event\UserWasRegistered::class => [
\App\src\Prooph\ProophessorDo\Projection\User\UserProjector::class
],
],
],
],*/
],
];
This is my service that dispatch the command
class ProcedureRetrieveStateChanged
{
/** #var \FluentProceduresRepository $procedureRepository */
private $procedureRepository;
/**
* #var CommandBus
*/
private $commandBus;
public function __construct(\FluentProceduresRepository $procedureRepository, CommandBus $commandBus)
{
$this->procedureRepository = $procedureRepository;
$this->commandBus = $commandBus;
}
public function execute($procedureId, array $groups)
{
$procedure = $this->procedureRepository->find($procedureId);
if (null === $procedure) {
return false;
}
foreach ($groups as $group) {
$actualState = $procedure->getStatebyGroup($group['pivot']['group_id']);
if ($actualState !== $group['pivot']['state']) {
$command = new ChangeProcedureState(
[
'procedure_id' => $procedureId,
'group_id' => $group['pivot']['group_id'],
'old_state' => $actualState,
'new_state' => $group['pivot']['state'],
]
);
$this->commandBus->dispatch($command);
}
}
}
}
This is my command
final class ChangeProcedureState extends Command implements PayloadConstructable
{
use PayloadTrait;
public function procedureId(): ProcedureId
{
return ProcedureId::fromString($this->payload['procedure_id']);
}
public function state(): string
{
return $this->payload['state'];
}
protected function setPayload(array $payload): void
{
Assertion::keyExists($payload, 'procedure_id');
Assertion::keyExists($payload, 'group_id');
Assertion::keyExists($payload, 'old_state');
Assertion::keyExists($payload, 'new_state');
$this->payload = $payload;
}
}
And this is my handler
class ChangeProcedureStateHandler
{
/**
* #var ProcedureCollection
*/
private $procedureCollection;
public function __construct(ProcedureCollection $procedureCollection)
{
$this->procedureCollection = $procedureCollection;
}
public function __invoke(ChangeProcedureState $command): void
{
$procedure = $this->procedureCollection->get($command->procedureId());
$procedure->changeState($command->state());
$this->procedureCollection->save($procedure);
}
}
Can someone help me with this problem?
To use handlers as a string you need to use ServiceLocatorPlugin, but before that you need to register all handlers in laravel container, like $app->singletor...or $app->bind.
Cheers.
I don't understand why this error occur.
get error on call a cms
http://localhost/yii-cms/web/cms
Calling unknown method: yii2mod\cms\controllers\CmsController::setInstance()
i am try to use of yii2-cms
cmsController
<?php
namespace yii2mod\cms\controllers;
use Yii;
use yii2mod\cms\models\CmsModel;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii2mod\cms\models\search\CmsModelSearch;
use yii2mod\editable\EditableAction;
use yii2mod\toggle\actions\ToggleAction;
/**
* Class CmsController
* #package yii2mod\cms\controllers
*/
class CmsController extends Controller
{
/**
* #var string view path
*/
public $viewPath = '#vendor/yii2mod/yii2-cms/views/cms/';
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'index' => ['get'],
'create' => ['get', 'post'],
'update' => ['get', 'post'],
'delete' => ['post']
],
]
];
}
/**
* #inheritdoc
*/
public function actions()
{
return [
'edit-page' => [
'class' => EditableAction::className(),
'modelClass' => CmsModel::className(),
'forceCreate' => false
],
'toggle' => [
'class' => ToggleAction::className(),
'modelClass' => CmsModel::className(),
]
];
}
/**
* Lists all CmsModel models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new CmsModelSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render($this->viewPath . 'index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]);
}
/**
* Creates a new CmsModel model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new CmsModel();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', Yii::t('yii2mod.cms', 'Page has been created.'));
return $this->redirect(['index']);
}
return $this->render($this->viewPath . 'create', [
'model' => $model,
]);
}
/**
* Updates an existing CmsModel model.
* If update is successful, the browser will be redirected to the 'view' page.
*
* #param integer $id
*
* #return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', Yii::t('yii2mod.cms', 'Page has been updated.'));
return $this->redirect(['index']);
}
return $this->render($this->viewPath . 'update', [
'model' => $model,
]);
}
/**
* Deletes an existing CmsModel model.
* If deletion is successful, the browser will be redirected to the 'index' page.
*
* #param integer $id
*
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
Yii::$app->session->setFlash('success', Yii::t('yii2mod.cms', 'Page has been deleted.'));
return $this->redirect(['index']);
}
/**
* Finds the CmsModel model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
*
* #param integer $id
*
* #return CmsModel the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = CmsModel::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException(Yii::t('yii2mod.cms', 'The requested page does not exist.'));
}
}
}
i have following yii2-cms and it's work great
set instance error occur due to they can not find out given class and that's possible due to miss configuration.
follow Configuration Link https://github.com/yii2mod/yii2-cms#configuration
1) To use this extension first you need to configure the comments extension, after that you have to configure the main config in your application:
'modules' => [
'admin' => [
'controllerMap' => [
'cms' => 'yii2mod\cms\controllers\CmsController'
// You can set your template files
// 'layout' => '#app/modules/backend/views/layouts/main',
// 'viewPath' => '#app/modules/backend/views/cms/',
],
],
],
You can then access to management section through the following URL:
http://localhost/path/to/index.php?r=admin/cms/index
2) Add new Rule class to the urlManager array in your application configuration by the following code:
'components' => [
'urlManager' => [
'rules' => [
['class' => 'yii2mod\cms\components\PageUrlRule'],
]
],
],
3) Add to SiteController (or configure via $route param in urlManager):
/**
* #return array
*/
public function actions()
{
return [
'page' => [
'class' => 'yii2mod\cms\actions\PageAction',
// You can set your template files
'view' => '#app/views/site/page'
],
];
}
And now you can create your own pages via the admin panel, and access them via the url of each page.
Yes error is solved by following proper Configuration STEP.
Error is occur due to miss configure second step
2) Add new Rule class to the urlManager array in your application configuration by the following code:
'components' => [
'urlManager' => [
'rules' => [
['class' => 'yii2mod\cms\components\PageUrlRule'],
]
],
],
Full Configuration you need to did :
1) To use this extension first you need to configure the comments extension, after that you have to configure the main config in your application:
'modules' => [
'admin' => [
'controllerMap' => [
'cms' => 'yii2mod\cms\controllers\CmsController'
// You can set your template files
// 'layout' => '#app/modules/backend/views/layouts/main',
// 'viewPath' => '#app/modules/backend/views/cms/',
],
],
],
You can then access to management section through the following URL:
http://localhost/path/to/index.php?r=admin/cms/index
2) Add new Rule class to the urlManager array in your application configuration by the following code:
'components' => [
'urlManager' => [
'rules' => [
['class' => 'yii2mod\cms\components\PageUrlRule'],
]
],
],
3) Add to SiteController (or configure via $route param in urlManager):
/**
* #return array
*/
public function actions()
{
return [
'page' => [
'class' => 'yii2mod\cms\actions\PageAction',
// You can set your template files
'view' => '#app/views/site/page'
],
];
}
And now you can create your own pages via the admin panel, and access them via the url of each page.
You seem to be using the cms extension in your site component as-is.
In your web.php file, add this:
'components' => [
...
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '#app/messages'
'sourceLanguage' => 'en',
],
],
],
]
...,
'controllerMap': => [
'cms' => 'yii2mod\cms\controllers\CmsController'
],
NOTE: You should exclude the path about configuring it as a component in the admin module since you're not using it anyway.
If you were using it in a module, then the steps documented in the README is just fine for you.
I'd like to keep users away from editing configuration files, so I've made web interface in admin panel for setting up Mail server, username, password, port, encryption..
I was working well in Laravel 4.2, but now when the app has been rewritten into Laravel 5, an error occurs:
Class 'Settings' not found in <b>F:\htdocs\app\config\mail.php</b> on line <b>18</b><br />
For this purpose I've created a service provider, made a facade, put them in config/app.php, Settings::get('var')/Settings::set('var') work perfectly, but not for mail settings.
config/mail.php:
<?php return array(
'driver' => Settings::get('mail_driver'),
'host' => Settings::get('mail_host'),
'port' => Settings::get('mail_port'),
'from' => array('address' => Settings::get('mail_from_address'), 'name' => Settings::get('mail_from_name')),
'encryption' => Settings::get('mail_encryption'),
'username' => Settings::get('mail_username'),
'password' => Settings::get('mail_password'),
'sendmail' => Settings::get('mail_sendmail'),
'pretend' => false,
);
config/app.php:
'providers' => [
...
'App\Providers\SettingsServiceProvider',
...
'aliases' => [
...
'Settings' => 'App\Custom\Facades\Settings',
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Custom\Settings;
class SettingsServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton('settings', function()
{
return new Settings;
});
}
}
<?php namespace App\Custom;
use App\Setting;
class Settings {
public function get($var) {
try{
$setting = Setting::first();
} catch(exception $e)
{
return false;
}
return $setting->$var;
}
public function set($var, $val) {
try{
$setting = Setting::first();
$setting->$var = $val;
$setting->save();
} catch(exception $e)
{
return false;
}
return true;
}
}
<?php
namespace App\Custom\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() { return 'settings'; }
}
Any ideas how to implement Laravel mail settings using database?
To archive this I created CustomMailServiceProvider by extending Illuminate\Mail\MailServiceProvider so as to overwrite this method:
protected function registerSwiftTransport(){
$this->app['swift.transport'] = $this->app->share(function($app)
{
return new TransportManager($app);
});
}
Here is the complete solution
I created CustomMailServiceProvider.php in app\Providers
namespace App\Providers;
use Illuminate\Mail\MailServiceProvider;
use App\Customs\CustomTransportManager;
class CustomMailServiceProvider extends MailServiceProvider{
protected function registerSwiftTransport(){
$this->app['swift.transport'] = $this->app->share(function($app)
{
return new CustomTransportManager($app);
});
}
}
I created CustomTransportManager.php in app/customs directory -
NB: app/customs directory doesn't exist in default laravel 5 directory structure, I created it
namespace App\Customs;
use Illuminate\Mail\TransportManager;
use App\Models\Setting; //my models are located in app\models
class CustomTransportManager extends TransportManager {
/**
* Create a new manager instance.
*
* #param \Illuminate\Foundation\Application $app
* #return void
*/
public function __construct($app)
{
$this->app = $app;
if( $settings = Setting::all() ){
$this->app['config']['mail'] = [
'driver' => $settings->mail_driver,
'host' => $settings->mail_host,
'port' => $settings->mail_port,
'from' => [
'address' => $settings->mail_from_address,
'name' => $settings->mail_from_name
],
'encryption' => $settings->mail_encryption,
'username' => $settings->mail_username,
'password' => $settings->mail_password,
'sendmail' => $settings->mail_sendmail,
'pretend' => $settings->mail_pretend
];
}
}
}
And finally, I replaced 'Illuminate\Mail\MailServiceProvider', in config/app.php with 'App\Providers\CustomMailServiceProvider',
I have added
$this->app['config']['services'] = [
'mailgun' => [
'domain' => $settings->mailgun_domain,
'secret' => $settings->mailgun_secret,
]
];
to CustomTransportManager __construct() to include mailgun API credentials that I'm using as mailing service
I configured as mentioned, however got the following error. While I tried your code found that from Laravel 5.4 share method is deprecated and instead informed to use singleton.
Call to undefined method Illuminate\Foundation\Application::share()
here is the below method using singleton instead using share method:
protected function registerSwiftTransport(){
$this->app->singleton('swift.transport', function ($app){
return new CustomTransportManager($app);
});
}
#DigitLimit , method share() has been dropped since Laravel 5.4. I had to work-around this problem using other methods, and I am not sure they are perfect. Here is my registerSwiftTransport() method in CustomMailServiceProvider class.
Firstly, we need to determine if code is not executed while calling app through command line: "if(strpos(php_sapi_name(), 'cli') === false)". If we don't check that and don't prevent setting new params in this case, Artisan will throw us errors in command line. Secondly, we need to get settings from database somehow. I did it using my method getSettingValue(), where first argument is setting key, and second argument is default value if setting is not found. As you see, I assigned settings to $this->app['config']['mail'].
After that, I used singleton() method:
protected function registerSwiftTransport(){
if (strpos(php_sapi_name(), 'cli') === false) {
$this->app['config']['mail'] = [
'driver' => Setting::getSettingValue('mail_driver', '****'),
'host' => Setting::getSettingValue('mail_host', '****'),
'port' => Setting::getSettingValue('mail_port', 25),
'from' => [
'address' => Setting::getSettingValue('mail_from_address', '****'),
'name' => Setting::getSettingValue('mail_from_name', '****'),
],
'encryption' => Setting::getSettingValue('mail_encryption', '***'),
'username' => Setting::getSettingValue('mail_username', '****'),
'password' => Setting::getSettingValue('mail_password', '****'),
];
}
$this->app->singleton('swift.transport', function ($app) {
return new Illuminate\Mail\TransportManager($app);
});
}
I managed to write the REST API code and it works for the standard actions.
Now, if I want to send more attributes, like url_to_api_action?a=b&c=d&e=f, this does not match any of the standard actions.
I need to search by attributes, using a RESTful API in Yii2.
any ideas?
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
class UneController extends ActiveController {
public $modelClass = 'common\models\Une';
}
I'm elaborating the answer
add search action mentioned in this link to the controller
Yii2 REST query
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
use yii\data\ActiveDataProvider;
/**
* Country Controller API
*
* #author Budi Irawan <deerawan#gmail.com>
*/
class CountryController extends ActiveController
{
public $modelClass = 'api\modules\v1\models\Country';
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
public function actionSearch()
{
if (!empty($_GET)) {
$model = new $this->modelClass;
foreach ($_GET as $key => $value) {
if (!$model->hasAttribute($key)) {
throw new \yii\web\HttpException(404, 'Invalid attribute:' . $key);
}
}
try {
$provider = new ActiveDataProvider([
'query' => $model->find()->where($_GET),
'pagination' => false
]);
} catch (Exception $ex) {
throw new \yii\web\HttpException(500, 'Internal server error');
}
if ($provider->getCount() <= 0) {
throw new \yii\web\HttpException(404, 'No entries found with this query string');
}
else {
return $provider;
}
}
else {
throw new \yii\web\HttpException(400, 'There are no query string');
}
}
}
And add urlManager like below in the config/main.php
Cant use tockens and extrapattern together for REST services in Yii2
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'v1/country',
'extraPatterns' => [
'GET search' => 'search'
],
],
[
'class' => 'yii\rest\UrlRule',
'controller' => 'v1/country',
'tokens' => [
'{id}' => '<id:\\w+>'
]
],
],
]
therefor we can use both default actions of the activecontroller and our custom actions together
You can create your own actions inside the controller, and you just need to return the result from Active Record and it will take care of formatting the data.
public function actionSearch($keyword)
{
$result = YourModel::find()
->where(['keyword' => $keyword])
->all();
return $result;
}
More details here: http://www.yiiframework.com/doc-2.0/guide-rest.html#creating-controllers-and-actions
public function actionSearch($keyword)
{
$result = YourModel::find()
->with('model relation')
->where(['keyword' => $keyword])
->all();
return $result;
}